Merge branch 'master' into beta

# Conflicts:
#	submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift
#	versions.json
This commit is contained in:
Isaac 2026-04-07 20:45:57 +02:00
commit 41c6274252
321 changed files with 37450 additions and 5321 deletions

View file

@ -22,12 +22,10 @@ internal:
- export PATH=/opt/homebrew/opt/ruby/bin:$PATH
- export PATH=`gem environment gemdir`/bin:$PATH
- python3 -u build-system/Make/Make.py remote-build --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --cacheHost="$TELEGRAM_BAZEL_CACHE_HOST" --configurationPath="build-system/appcenter-configuration.json" --gitCodesigningRepository="$TELEGRAM_GIT_CODESIGNING_REPOSITORY" --gitCodesigningType=adhoc --configuration=release_arm64
- python3 -u build-system/Make/DeployToFirebase.py --configuration="$TELEGRAM_PRIVATE_DATA_PATH/firebase-configurations/firebase-internal.json" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
- python3 -u build-system/Make/DeployBuild.py --configuration="$TELEGRAM_PRIVATE_DATA_PATH/deploy-configurations/internal-configuration.json" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
- rm -rf build-input/configuration-repository-workdir
- rm -rf build-input/configuration-repository
- python3 -u build-system/Make/Make.py remote-build --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --cacheHost="$TELEGRAM_BAZEL_CACHE_HOST" --configurationPath="$TELEGRAM_PRIVATE_DATA_PATH/build-configurations/enterprise-configuration.json" --gitCodesigningRepository="$TELEGRAM_GIT_CODESIGNING_REPOSITORY" --gitCodesigningType=enterprise --configuration=release_arm64
- python3 -u build-system/Make/DeployToFirebase.py --configuration="$TELEGRAM_PRIVATE_DATA_PATH/firebase-configurations/firebase-enterprise.json" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
- python3 -u build-system/Make/DeployBuild.py --configuration="$TELEGRAM_PRIVATE_DATA_PATH/deploy-configurations/enterprise-configuration.json" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
environment:
name: internal

313
MODULE.bazel.lock generated
View file

@ -22,11 +22,12 @@
"https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
"https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65",
"https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
"https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
"https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
"https://bcr.bazel.build/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6",
"https://bcr.bazel.build/modules/bazel_features/1.33.0/source.json": "13617db3930328c2cd2807a0f13d52ca870ac05f96db9668655113265147b2a6",
"https://bcr.bazel.build/modules/bazel_features/1.36.0/MODULE.bazel": "596cb62090b039caf1cad1d52a8bc35cf188ca9a4e279a828005e7ee49a1bec3",
"https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
"https://bcr.bazel.build/modules/bazel_features/1.42.1/MODULE.bazel": "275a59b5406ff18c01739860aa70ad7ccb3cfb474579411decca11c93b951080",
"https://bcr.bazel.build/modules/bazel_features/1.42.1/source.json": "fcd4396b2df85f64f2b3bb436ad870793ecf39180f1d796f913cc9276d355309",
"https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
"https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
@ -40,7 +41,9 @@
"https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.1/source.json": "7ebaefba0b03efe59cac88ed5bbc67bcf59a3eff33af937345ede2a38b2d368a",
"https://bcr.bazel.build/modules/bazel_skylib/1.8.2/MODULE.bazel": "69ad6927098316848b34a9142bcc975e018ba27f08c4ff403f50c1b6e646ca67",
"https://bcr.bazel.build/modules/bazel_skylib/1.9.0/MODULE.bazel": "72997b29dfd95c3fa0d0c48322d05590418edef451f8db8db5509c57875fb4b7",
"https://bcr.bazel.build/modules/bazel_skylib/1.9.0/source.json": "7ad77c1e8c1b84222d9b3f3cae016a76639435744c19330b0b37c0a3c9da7dc0",
"https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
"https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
"https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
@ -58,23 +61,23 @@
"https://bcr.bazel.build/modules/nlohmann_json/3.12.0.bcr.1/source.json": "93f82a5ae985eb935c539bfee95e04767187818189241ac956f3ccadbdb8fb02",
"https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
"https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
"https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
"https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
"https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
"https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
"https://bcr.bazel.build/modules/platforms/1.0.0/MODULE.bazel": "f05feb42b48f1b3c225e4ccf351f367be0371411a803198ec34a389fb22aa580",
"https://bcr.bazel.build/modules/platforms/1.0.0/source.json": "f4ff1fd412e0246fd38c82328eb209130ead81d62dcd5a9e40910f867f733d96",
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
"https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
"https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
"https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
"https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92",
"https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
"https://bcr.bazel.build/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95",
"https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
"https://bcr.bazel.build/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42",
"https://bcr.bazel.build/modules/protobuf/33.4/source.json": "555f8686b4c7d6b5ba731fbea13bf656b4bfd9a7ff629c1d9d3f6e1d6155de79",
"https://bcr.bazel.build/modules/protobuf/34.0.bcr.1/MODULE.bazel": "74e541b0ba877813da786a11707d4e394433c157841d5111a36be0d44b907931",
"https://bcr.bazel.build/modules/protobuf/34.0.bcr.1/source.json": "fc174b3d6215aa14197d1bd779f98bb72d9fd666ee5ec0d6bba6ae986baa4535",
"https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
"https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34",
"https://bcr.bazel.build/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680",
@ -96,10 +99,11 @@
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
"https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0",
"https://bcr.bazel.build/modules/rules_cc/0.1.5/MODULE.bazel": "88dfc9361e8b5ae1008ac38f7cdfd45ad738e4fa676a3ad67d19204f045a1fd8",
"https://bcr.bazel.build/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8",
"https://bcr.bazel.build/modules/rules_cc/0.2.15/MODULE.bazel": "6a0a4a75a57aa6dc888300d848053a58c6b12a29f89d4304e1c41448514ec6e8",
"https://bcr.bazel.build/modules/rules_cc/0.2.15/source.json": "197965c6dcca5c98a9288f93849e2e1c69d622e71b0be8deb524e22d48c88e32",
"https://bcr.bazel.build/modules/rules_cc/0.2.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84",
"https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
@ -140,23 +144,22 @@
"https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483",
"https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73",
"https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2",
"https://bcr.bazel.build/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96",
"https://bcr.bazel.build/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e",
"https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1",
"https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
"https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
"https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
"https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53",
"https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
"https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
"https://bcr.bazel.build/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937",
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
"https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
"https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
"https://bcr.bazel.build/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8",
"https://bcr.bazel.build/modules/rules_python/1.6.0/source.json": "e980f654cf66ec4928672f41fc66c4102b5ea54286acf4aecd23256c84211be6",
"https://bcr.bazel.build/modules/rules_python/1.7.0/MODULE.bazel": "d01f995ecd137abf30238ad9ce97f8fc3ac57289c8b24bd0bf53324d937a14f8",
"https://bcr.bazel.build/modules/rules_python/1.7.0/source.json": "028a084b65dcf8f4dc4f82f8778dbe65df133f234b316828a82e060d81bdce32",
"https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
"https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
"https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3",
"https://bcr.bazel.build/modules/rules_shell/0.6.1/MODULE.bazel": "72e76b0eea4e81611ef5452aa82b3da34caca0c8b7b5c0c9584338aa93bae26b",
"https://bcr.bazel.build/modules/rules_shell/0.6.1/source.json": "20ec05cd5e592055e214b2da8ccb283c7f2a421ea0dc2acbf1aa792e11c03d0c",
"https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
"https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
"https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef",
@ -164,8 +167,8 @@
"https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
"https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f",
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b",
"https://bcr.bazel.build/modules/swift_argument_parser/1.7.0/MODULE.bazel": "40d4e44950e44973dcf8590bcee637591de196b5dbe3696d07eb342b74d53672",
"https://bcr.bazel.build/modules/swift_argument_parser/1.7.0/source.json": "b9b952cba0c748083b9b891e6ac46d347c92d37e8a92ead96d2a54b966bacd87",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
@ -238,10 +241,245 @@
]
}
},
"@@rules_python+//python/extensions:config.bzl%config": {
"general": {
"bzlTransitiveDigest": "xaCns8Qt+8bJqVLy8r6nc/eL2AjEIX/vOdjqoh5xYac=",
"usagesDigest": "ZVSXMAGpD+xzVNPuvF1IoLBkty7TROO0+akMapt1pAg=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"rules_python_internal": {
"repoRuleId": "@@rules_python+//python/private:internal_config_repo.bzl%internal_config_repo",
"attributes": {
"transition_setting_generators": {},
"transition_settings": []
}
},
"pypi__build": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/e2/03/f3c8ba0a6b6e30d7d18c40faab90807c9bb5e9a1e3b2fe2008af624a9c97/build-1.2.1-py3-none-any.whl",
"sha256": "75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__click": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl",
"sha256": "ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__colorama": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl",
"sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__importlib_metadata": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/2d/0a/679461c511447ffaf176567d5c496d1de27cbe34a87df6677d7171b2fbd4/importlib_metadata-7.1.0-py3-none-any.whl",
"sha256": "30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__installer": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl",
"sha256": "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__more_itertools": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/50/e2/8e10e465ee3987bb7c9ab69efb91d867d93959095f4807db102d07995d94/more_itertools-10.2.0-py3-none-any.whl",
"sha256": "686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__packaging": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/49/df/1fceb2f8900f8639e278b056416d49134fb8d84c5942ffaa01ad34782422/packaging-24.0-py3-none-any.whl",
"sha256": "2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pep517": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/25/6e/ca4a5434eb0e502210f591b97537d322546e4833dcb4d470a48c375c5540/pep517-0.13.1-py3-none-any.whl",
"sha256": "31b206f67165b3536dd577c5c3f1518e8fbaf38cbc57efff8369a392feff1721",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pip": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/8a/6a/19e9fe04fca059ccf770861c7d5721ab4c2aebc539889e97c7977528a53b/pip-24.0-py3-none-any.whl",
"sha256": "ba0d021a166865d2265246961bec0152ff124de910c5cc39f1156ce3fa7c69dc",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pip_tools": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/0d/dc/38f4ce065e92c66f058ea7a368a9c5de4e702272b479c0992059f7693941/pip_tools-7.4.1-py3-none-any.whl",
"sha256": "4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__pyproject_hooks": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/ae/f3/431b9d5fe7d14af7a32340792ef43b8a714e7726f1d7b69cc4e8e7a3f1d7/pyproject_hooks-1.1.0-py3-none-any.whl",
"sha256": "7ceeefe9aec63a1064c18d939bdc3adf2d8aa1988a510afec15151578b232aa2",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__setuptools": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/90/99/158ad0609729111163fc1f674a5a42f2605371a4cf036d0441070e2f7455/setuptools-78.1.1-py3-none-any.whl",
"sha256": "c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__tomli": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl",
"sha256": "939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__wheel": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/7d/cd/d7460c9a869b16c3dd4e1e403cce337df165368c71d6af229a74699622ce/wheel-0.43.0-py3-none-any.whl",
"sha256": "55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
},
"pypi__zipp": {
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
"attributes": {
"url": "https://files.pythonhosted.org/packages/da/55/a03fd7240714916507e1fcf7ae355bd9d9ed2e6db492595f1a67f61681be/zipp-3.18.2-py3-none-any.whl",
"sha256": "dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e",
"type": "zip",
"build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nload(\"@rules_python//python:py_library.bzl\", \"py_library\")\n\npy_library(\n name = \"lib\",\n srcs = glob([\"**/*.py\"]),\n data = glob([\"**/*\"], exclude=[\n # These entries include those put into user-installed dependencies by\n # data_exclude to avoid non-determinism.\n \"**/*.py\",\n \"**/*.pyc\",\n \"**/*.pyc.*\", # During pyc creation, temp files named *.pyc.NNN are created\n \"**/*.dist-info/RECORD\",\n \"BUILD\",\n \"WORKSPACE\",\n ]),\n # This makes this directory a top-level in the python import\n # search path for anything that depends on this.\n imports = [\".\"],\n)\n"
}
}
},
"recordedRepoMappingEntries": [
[
"rules_python+",
"bazel_tools",
"bazel_tools"
],
[
"rules_python+",
"pypi__build",
"rules_python++config+pypi__build"
],
[
"rules_python+",
"pypi__click",
"rules_python++config+pypi__click"
],
[
"rules_python+",
"pypi__colorama",
"rules_python++config+pypi__colorama"
],
[
"rules_python+",
"pypi__importlib_metadata",
"rules_python++config+pypi__importlib_metadata"
],
[
"rules_python+",
"pypi__installer",
"rules_python++config+pypi__installer"
],
[
"rules_python+",
"pypi__more_itertools",
"rules_python++config+pypi__more_itertools"
],
[
"rules_python+",
"pypi__packaging",
"rules_python++config+pypi__packaging"
],
[
"rules_python+",
"pypi__pep517",
"rules_python++config+pypi__pep517"
],
[
"rules_python+",
"pypi__pip",
"rules_python++config+pypi__pip"
],
[
"rules_python+",
"pypi__pip_tools",
"rules_python++config+pypi__pip_tools"
],
[
"rules_python+",
"pypi__pyproject_hooks",
"rules_python++config+pypi__pyproject_hooks"
],
[
"rules_python+",
"pypi__setuptools",
"rules_python++config+pypi__setuptools"
],
[
"rules_python+",
"pypi__tomli",
"rules_python++config+pypi__tomli"
],
[
"rules_python+",
"pypi__wheel",
"rules_python++config+pypi__wheel"
],
[
"rules_python+",
"pypi__zipp",
"rules_python++config+pypi__zipp"
]
]
}
},
"@@rules_python+//python/uv:uv.bzl%uv": {
"general": {
"bzlTransitiveDigest": "PmZM/pIkZKEDDL68TohlKJrWPYKL5VwUw3MA7kmm6fk=",
"usagesDigest": "p80sy6cYQuWxx5jhV3fOTu+N9EyIUFG9+F7UC/nhXic=",
"bzlTransitiveDigest": "N8SCcKcL6KnzBLApxvY2jR9vhXjA2VCBZMLZfY3sDRA=",
"usagesDigest": "H8dQoNZcoqP+Mu0tHZTi4KHATzvNkM5ePuEqoQdklIU=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@ -278,43 +516,6 @@
]
]
}
},
"@@rules_xcodeproj+//xcodeproj:extensions.bzl%internal": {
"general": {
"bzlTransitiveDigest": "+qmLBZzimJ0CYyKoQg6/pbdkTnu/s4e5IisoM+TLM+8=",
"usagesDigest": "fvsnMonVwKDYnBfww4bXuYie3WU0d9VSqT2gePSdQco=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"rules_xcodeproj_generated": {
"repoRuleId": "@@rules_xcodeproj+//xcodeproj:repositories.bzl%generated_files_repo",
"attributes": {}
}
},
"recordedRepoMappingEntries": [
[
"bazel_features+",
"bazel_features_globals",
"bazel_features++version_extension+bazel_features_globals"
],
[
"bazel_features+",
"bazel_features_version",
"bazel_features++version_extension+bazel_features_version"
],
[
"rules_xcodeproj+",
"bazel_features",
"bazel_features+"
],
[
"rules_xcodeproj+",
"bazel_tools",
"bazel_tools"
]
]
}
}
}
}

View file

@ -815,7 +815,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo
if let searchTerm = searchTerm {
if !searchTerm.isEmpty {
for renderedPeer in transaction.searchPeers(query: searchTerm) {
for renderedPeer in transaction.searchPeers(query: searchTerm, predicate: nil) {
if let peer = renderedPeer.peer, !(peer is TelegramSecretChat), !peer.isDeleted {
peers.append(peer)
}
@ -988,7 +988,7 @@ private final class WidgetIntentHandler {
if let searchTerm = searchTerm {
if !searchTerm.isEmpty {
for renderedPeer in transaction.searchPeers(query: searchTerm) {
for renderedPeer in transaction.searchPeers(query: searchTerm, predicate: nil) {
if let peer = renderedPeer.peer, !(peer is TelegramSecretChat), !peer.isDeleted {
peers.append(peer)
}

View file

@ -16169,3 +16169,10 @@ Error: %8$@";
"PeerInfo.UnofficialSecurityRisk" = "%@ uses an unofficial Telegram client messages to this user may be less secure.";
"Gallery.Live" = "LIVE";
"CreatePoll.QuestionNeeded" = "Enter a question";
"CreatePoll.OptionsNeeded" = "Add at least two options";
"CreatePoll.QuizCorrectOptionNeeded" = "Select a correct option";
"CreatePoll.QuizCorrectOptionNeededMultiple" = "Select at least one correct option";
"Stars.Intro.Transaction.Commission.Title" = "%@ commission";

View file

@ -31,7 +31,7 @@ class UITests: XCTestCase {
}
func testSignUp() throws {
deleteTestAccount(phone: "9996629999")
deleteTestAccount(phone: "9996625296")
app.launch()
// Welcome screen tap Start Messaging

View file

@ -31,6 +31,7 @@ swift_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/Display:Display",
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
"//submodules/TelegramUI/Components/DCTAnimationCacheImpl:DCTAnimationCacheImpl",
"//submodules/TelegramUI/Components/VideoAnimationCache:VideoAnimationCache",
"//submodules/TelegramUI/Components/LottieAnimationCache:LottieAnimationCache",
"//submodules/rlottie:RLottieBinding",

View file

@ -3,6 +3,7 @@ import UIKit
import Display
import AnimationCache
import DCTAnimationCacheImpl
import SwiftSignalKit
import VideoAnimationCache
import LottieAnimationCache
@ -50,7 +51,7 @@ public final class ViewController: UIViewController {
let basePath = NSTemporaryDirectory() + "/animation-cache"
let _ = try? FileManager.default.removeItem(atPath: basePath)
let _ = try? FileManager.default.createDirectory(at: URL(fileURLWithPath: basePath), withIntermediateDirectories: true)
self.cache = AnimationCacheImpl(basePath: basePath, allocateTempFile: {
self.cache = DCTAnimationCacheImpl(basePath: basePath, allocateTempFile: {
return basePath + "/\(Int64.random(in: 0 ... Int64.max))"
})

View file

@ -6,6 +6,7 @@ import sys
import json
import hashlib
import base64
import time
import requests
def sha256_file(path):
@ -26,16 +27,55 @@ def init_build(host, token, files, channel):
r.raise_for_status()
return r.json()
class ProgressFileReader:
def __init__(self, path, size):
self._file = open(path, 'rb')
self._size = size
self._sent = 0
self._start_time = time.time()
self._last_print = self._start_time
def __len__(self):
return self._size
def read(self, chunk_size=-1):
if chunk_size == -1:
chunk_size = self._size
data = self._file.read(chunk_size)
if not data:
elapsed = time.time() - self._start_time
speed = self._size / elapsed / 1024 / 1024 if elapsed > 0 else 0
print(' 100% - all bytes sent in {:.1f}s ({:.2f} MB/s), waiting for server response...'.format(elapsed, speed))
return data
self._sent += len(data)
now = time.time()
if now - self._last_print >= 5:
elapsed = now - self._start_time
speed = self._sent / elapsed / 1024 / 1024 if elapsed > 0 else 0
print(' {:.1f}% ({:.1f} / {:.1f} MB) {:.2f} MB/s'.format(
self._sent * 100 / self._size, self._sent / 1024 / 1024, self._size / 1024 / 1024, speed))
self._last_print = now
return data
def close(self):
self._file.close()
def upload_file(path, upload_info):
url = upload_info.get('url')
headers = dict(upload_info.get('headers', {}))
size = os.path.getsize(path)
headers['Content-Length'] = str(size)
print('Uploading', path)
with open(path, 'rb') as f:
r = requests.put(url, data=f, headers=headers, timeout=900)
print('Uploading {} ({:.1f} MB)'.format(path, size / 1024 / 1024))
start_time = time.time()
body = ProgressFileReader(path, size)
try:
r = requests.put(url, data=body, headers=headers, timeout=900)
finally:
body.close()
print(' Server responded: {} ({:.1f}s total)'.format(r.status_code, time.time() - start_time))
if r.status_code != 200:
print('Upload failed', r.status_code)
print(r.text[:500])

@ -1 +1 @@
Subproject commit 7967e453d1f54963935a1ae06808e1c64420bfb1
Subproject commit a99414ad848c3aeb84640934352ecc85d8a937f5

@ -1 +1 @@
Subproject commit f6aef7db5b649ffc3b7497ecc83a08ad3ab19559
Subproject commit 1791d916de4083388f22e20248d8b010d23f0d6b

@ -1 +1 @@
Subproject commit 2b9261134a123ad83f8b1d2dc7fc69ae83fe7172
Subproject commit 9dce728ed1e9168ec8c912fcd3443dad48a286fe

@ -1 +1 @@
Subproject commit 79f26ec1315531a8459fe95934440cb3ca3c16df
Subproject commit 997f2db058596f91663e54782b79490de87208da

View file

@ -1445,7 +1445,7 @@ public protocol SharedAccountContext: AnyObject {
func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolvedUrl, NoError>
func resolveUrlWithProgress(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal<ResolveUrlResult, NoError>
func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, forceUpdate: Bool, openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, UIView?, CGRect?) -> Bool)?, sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?, progress: Promise<Bool>?, completion: (() -> Void)?)
func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
func openAddContact(context: AccountContext, peer: EnginePeer?, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void)
func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void)
func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void)
func openImagePicker(context: AccountContext, completion: @escaping (UIImage) -> Void, present: @escaping (ViewController) -> Void)
@ -1519,7 +1519,7 @@ public protocol SharedAccountContext: AnyObject {
func makeGiftDemoScreen(context: AccountContext) -> ViewController
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void, requestSelectMessages: ((String, Data, String?) -> Void)?)
func makeShareController(context: AccountContext, subject: ShareControllerSubject, forceExternal: Bool, shareStory: (() -> Void)?, enqueued: (([PeerId], [Int64]) -> Void)?, actionCompleted: (() -> Void)?) -> ViewController
func makeShareController(context: AccountContext, params: ShareControllerParams) -> ViewController
func makeMiniAppListScreenInitialData(context: AccountContext) -> Signal<MiniAppListScreenInitialData, NoError>
func makeMiniAppListScreen(context: AccountContext, initialData: MiniAppListScreenInitialData) -> ViewController
func makeIncomingMessagePrivacyScreen(context: AccountContext, value: GlobalPrivacySettings.NonContactChatsPrivacy, exceptions: SelectivePrivacySettings, update: @escaping (GlobalPrivacySettings.NonContactChatsPrivacy) -> Void) -> ViewController
@ -1550,7 +1550,7 @@ public protocol SharedAccountContext: AnyObject {
func openCreateGroupCallUI(context: AccountContext, peerIds: [EnginePeer.Id], parentController: ViewController)
func makeNewContactScreen(context: AccountContext, peer: EnginePeer?, phoneNumber: String?, shareViaException: Bool, completion: @escaping (EnginePeer?, DeviceContactStableId?, DeviceContactExtendedData?) -> Void) -> ViewController
func makeNewContactScreen(context: AccountContext, peer: EnginePeer?, firstName: String?, lastName: String?, phoneNumber: String?, shareViaException: Bool, completion: @escaping (EnginePeer?, DeviceContactStableId?, DeviceContactExtendedData?) -> Void) -> ViewController
func makeLoginEmailSetupController(context: AccountContext, blocking: Bool, emailPattern: String?, canAutoDismissIfNeeded: Bool, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController
func makePasskeySetupController(context: AccountContext, displaySkip: Bool, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController

View file

@ -67,6 +67,7 @@ public final class PeerSelectionControllerParams {
public let hasCreation: Bool
public let immediatelySwitchToContacts: Bool
public let immediatelyActivateMultipleSelection: Bool
public let suggestedPeers: [EnginePeer]
public init(
context: AccountContext,
@ -89,7 +90,8 @@ public final class PeerSelectionControllerParams {
selectForumThreads: Bool = false,
hasCreation: Bool = false,
immediatelySwitchToContacts: Bool = false,
immediatelyActivateMultipleSelection: Bool = false
immediatelyActivateMultipleSelection: Bool = false,
suggestedPeers: [EnginePeer] = []
) {
self.context = context
self.updatedPresentationData = updatedPresentationData
@ -112,6 +114,7 @@ public final class PeerSelectionControllerParams {
self.hasCreation = hasCreation
self.immediatelySwitchToContacts = immediatelySwitchToContacts
self.immediatelyActivateMultipleSelection = immediatelyActivateMultipleSelection
self.suggestedPeers = suggestedPeers
}
}

View file

@ -6,6 +6,7 @@ import TelegramPresentationData
import TelegramUIPreferences
import AnimationCache
import MultiAnimationRenderer
import Display
public enum StorySharingSubject {
case messages([Message])
@ -37,6 +38,43 @@ public protocol ShareControllerEnvironment: AnyObject {
func donateSendMessageIntent(account: ShareControllerAccountContext, peerIds: [EnginePeer.Id])
}
public final class ShareControllerAppAccountContext: ShareControllerAccountContext {
public let context: AccountContext
public var accountId: AccountRecordId {
return self.context.account.id
}
public var accountPeerId: EnginePeer.Id {
return self.context.account.stateManager.accountPeerId
}
public var stateManager: AccountStateManager {
return self.context.account.stateManager
}
public var engineData: TelegramEngine.EngineData {
return self.context.engine.data
}
public var animationCache: AnimationCache {
return self.context.animationCache
}
public var animationRenderer: MultiAnimationRenderer {
return self.context.animationRenderer
}
public var contentSettings: ContentSettings {
return self.context.currentContentSettings.with { $0 }
}
public var appConfiguration: AppConfiguration {
return self.context.currentAppConfiguration.with { $0 }
}
public init(context: AccountContext) {
self.context = context
}
public func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> {
return self.context.engine.stickers.resolveInlineStickers(fileIds: fileIds)
}
}
public enum ShareControllerExternalStatus {
case preparing(Bool)
case progress(Float)
@ -78,3 +116,110 @@ public enum ShareControllerSubject {
case mapMedia(TelegramMediaMap)
case fromExternal(Int, ([PeerId], [PeerId: Int64], [PeerId: StarsAmount], String, ShareControllerAccountContext, Bool) -> Signal<ShareControllerExternalStatus, ShareControllerError>)
}
public struct ShareControllerAction {
public let title: String
public let action: () -> Void
public init(title: String, action: @escaping () -> Void) {
self.title = title
self.action = action
}
}
public enum ShareControllerPreferredAction {
case `default`
case saveToCameraRoll
case custom(action: ShareControllerAction)
}
public struct ShareControllerSegmentedValue {
public let title: String
public let subject: ShareControllerSubject
public let actionTitle: String
public let formatSendTitle: (Int) -> String
public init(title: String, subject: ShareControllerSubject, actionTitle: String, formatSendTitle: @escaping (Int) -> String) {
self.title = title
self.subject = subject
self.actionTitle = actionTitle
self.formatSendTitle = formatSendTitle
}
}
public final class ShareControllerParams {
public let subject: ShareControllerSubject
public let presetText: String?
public let preferredAction: ShareControllerPreferredAction
public let showInChat: ((Message) -> Void)?
public let fromForeignApp: Bool
public let segmentedValues: [ShareControllerSegmentedValue]?
public let externalShare: Bool
public let immediateExternalShare: Bool
public let immediatePeerId: PeerId?
public let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
public let forceTheme: PresentationTheme?
public let forcedActionTitle: String?
public let shareAsLink: Bool
public let collectibleItemInfo: TelegramCollectibleItemInfo?
public let actionCompleted: (() -> Void)?
public let dismissed: ((Bool) -> Void)?
public let completed: (([PeerId]) -> Void)?
public let enqueued: (([PeerId], [Int64]) -> Void)?
public let shareStory: (() -> Void)?
public let debugAction: (() -> Void)?
public let onMediaTimestampLinkCopied: ((Int32?) -> Void)?
public weak var parentNavigationController: NavigationController?
public let canSendInHighQuality: Bool
public init(
subject: ShareControllerSubject,
presetText: String? = nil,
preferredAction: ShareControllerPreferredAction = .default,
showInChat: ((Message) -> Void)? = nil,
fromForeignApp: Bool = false,
segmentedValues: [ShareControllerSegmentedValue]? = nil,
externalShare: Bool = true,
immediateExternalShare: Bool = false,
immediatePeerId: PeerId? = nil,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
forceTheme: PresentationTheme? = nil,
forcedActionTitle: String? = nil,
shareAsLink: Bool = false,
collectibleItemInfo: TelegramCollectibleItemInfo? = nil,
actionCompleted: (() -> Void)? = nil,
dismissed: ((Bool) -> Void)? = nil,
completed: (([PeerId]) -> Void)? = nil,
enqueued: (([PeerId], [Int64]) -> Void)? = nil,
shareStory: (() -> Void)? = nil,
debugAction: (() -> Void)? = nil,
onMediaTimestampLinkCopied: ((Int32?) -> Void)? = nil,
parentNavigationController: NavigationController? = nil,
canSendInHighQuality: Bool = false
) {
self.subject = subject
self.presetText = presetText
self.preferredAction = preferredAction
self.showInChat = showInChat
self.fromForeignApp = fromForeignApp
self.segmentedValues = segmentedValues
self.externalShare = externalShare
self.immediateExternalShare = immediateExternalShare
self.immediatePeerId = immediatePeerId
self.updatedPresentationData = updatedPresentationData
self.forceTheme = forceTheme
self.forcedActionTitle = forcedActionTitle
self.shareAsLink = shareAsLink
self.collectibleItemInfo = collectibleItemInfo
self.actionCompleted = actionCompleted
self.dismissed = dismissed
self.completed = completed
self.enqueued = enqueued
self.shareStory = shareStory
self.debugAction = debugAction
self.onMediaTimestampLinkCopied = onMediaTimestampLinkCopied
self.parentNavigationController = parentNavigationController
self.canSendInHighQuality = canSendInHighQuality
}
}

View file

@ -3450,7 +3450,7 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (ASDisplayNodePerformanceMeasurements)performanceMeasurements
{
MutexLocker l(__instanceLock__);
ASDisplayNodePerformanceMeasurements measurements = { .layoutSpecNumberOfPasses = -1, .layoutSpecTotalTime = NAN, .layoutComputationNumberOfPasses = -1, .layoutComputationTotalTime = NAN };
ASDisplayNodePerformanceMeasurements measurements = { .layoutSpecTotalTime = NAN, .layoutSpecNumberOfPasses = -1, .layoutComputationTotalTime = NAN, .layoutComputationNumberOfPasses = -1 };
if (_measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec) {
measurements.layoutSpecNumberOfPasses = _layoutSpecNumberOfPasses;
measurements.layoutSpecTotalTime = _layoutSpecTotalTime;

View file

@ -163,6 +163,8 @@ namespace AS {
return success;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
void lock() {
switch (_type) {
case Plain:
@ -184,7 +186,10 @@ namespace AS {
}
DidLock();
}
#pragma clang diagnostic pop
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
void unlock() {
WillUnlock();
switch (_type) {
@ -206,6 +211,7 @@ namespace AS {
break;
}
}
#pragma clang diagnostic pop
void AssertHeld() {
ASDisplayNodeCAssert(_owner == std::this_thread::get_id(), @"Thread should hold lock");

View file

@ -2226,7 +2226,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecog
}
}, makeEntityInputView: self.makeEntityInputView)
if let data = self.context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_disable_ai_chat"] as? Double, value == 1.0 {
} else {
} else if let peerId = self.presentationInterfaceState.chatLocation.peerId, peerId.namespace != Namespaces.Peer.SecretChat {
textInputPanelNode.isAIEnabled = true
}
textInputPanelNode.interfaceInteraction = self.interfaceInteraction

View file

@ -34,7 +34,7 @@ private enum InnerState: Equatable {
public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme {
return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, style: .glass)
return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, accentButtonColor: theme.list.itemCheckColors.fillColor, accentForegroundColor: theme.list.itemCheckColors.foregroundColor, style: .glass)
}
private let sharedContext: SharedAccountContext

View file

@ -13,7 +13,6 @@ import AccountContext
import AppBundle
import PromptUI
import SafariServices
import ShareController
import UndoUI
import UrlEscaping
@ -410,10 +409,9 @@ final class BrowserDocumentContent: UIView, BrowserContent, WKNavigationDelegate
private func share(url: String) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let shareController = ShareController(context: self.context, subject: .url(url))
shareController.actionCompleted = { [weak self] in
let shareController = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .url(url), actionCompleted: { [weak self] in
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
self.present(shareController, nil)
}

View file

@ -16,7 +16,6 @@ import TranslateUI
import ContextUI
import Pasteboard
import SaveToCameraRoll
import ShareController
import SafariServices
import LocationUI
import OpenInExternalAppUI
@ -1182,7 +1181,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
}
}), ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuShare, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuShare), action: { [weak self] in
if let self, let (webPage, _) = self.webPage, let image = media.media._asMedia() as? TelegramMediaImage {
self.present(ShareController(context: self.context, subject: .image(image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.media(media: .webPage(webPage: WebpageReference(webPage), media: image), resource: $0.resource)) }))), nil)
self.present(self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .image(image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.media(media: .webPage(webPage: WebpageReference(webPage), media: image), resource: $0.resource)) })))), nil)
}
})], catchTapsOutside: true)
self.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
@ -1325,7 +1324,7 @@ final class BrowserInstantPageContent: UIView, BrowserContent, UIScrollViewDeleg
}
}), ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuShare, accessibilityLabel: strings.Conversation_ContextMenuShare), action: { [weak self] in
if let strongSelf = self, let (webPage, _) = strongSelf.webPage, case let .Loaded(content) = webPage.content {
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
strongSelf.present(strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .quote(text: text, url: content.url))), nil)
}
})]

View file

@ -12,7 +12,6 @@ import AccountContext
import AppBundle
import PromptUI
import SafariServices
import ShareController
import UndoUI
import UrlEscaping
import PDFKit
@ -549,10 +548,9 @@ final class BrowserPdfContent: UIView, BrowserContent, UIScrollViewDelegate, PDF
private func share(url: String) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let shareController = ShareController(context: self.context, subject: .url(url))
shareController.actionCompleted = { [weak self] in
let shareController = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .url(url), actionCompleted: { [weak self] in
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
self.present(shareController, nil)
}

View file

@ -9,7 +9,6 @@ import ComponentFlow
import ViewControllerComponent
import AccountContext
import ContextUI
import ShareController
import UndoUI
import BundleIconComponent
import TelegramUIPreferences
@ -576,8 +575,9 @@ public class BrowserScreen: ViewController, MinimizableController {
} else {
subject = .url(url)
}
let shareController = ShareController(context: self.context, subject: subject)
shareController.completed = { [weak self] peerIds in
let shareController = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: subject, actionCompleted: { [weak self] in
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}, completed: { [weak self] peerIds in
guard let self else {
return
}
@ -590,10 +590,10 @@ public class BrowserScreen: ViewController, MinimizableController {
guard let self else {
return
}
let peers = peerList.compactMap { $0 }
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == self.context.account.peerId && !isDocument {
@ -615,7 +615,7 @@ public class BrowserScreen: ViewController, MinimizableController {
text = ""
}
}
self.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { [weak self] action in
if savedMessages, let self, action == .info {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
@ -633,10 +633,7 @@ public class BrowserScreen: ViewController, MinimizableController {
return false
}), in: .current)
})
}
shareController.actionCompleted = { [weak self] in
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}))
self.controller?.present(shareController, in: .window(.root))
case .minimize:
self.minimize()

View file

@ -13,7 +13,7 @@ import AccountContext
import AppBundle
import PromptUI
import SafariServices
import ShareController
import UndoUI
import LottieComponent
import MultilineTextComponent
@ -1580,10 +1580,9 @@ final class BrowserWebContent: UIView, BrowserContent, WKNavigationDelegate, WKU
private func share(url: String) {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let shareController = ShareController(context: self.context, subject: .url(url))
shareController.actionCompleted = { [weak self] in
let shareController = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .url(url), actionCompleted: { [weak self] in
self?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
self.present(shareController, nil)
}

View file

@ -1789,7 +1789,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
self.chatListDisplayNode.requestAddContact = { [weak self] phoneNumber in
if let strongSelf = self {
strongSelf.view.endEditing(true)
strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in
strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, peer: nil, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in
self?.present(controller, in: .window(.root), with: arguments)
}, pushController: { [weak self] controller in
(self?.navigationController as? NavigationController)?.pushViewController(controller)
@ -7177,9 +7177,6 @@ private final class ChatListLocationContext {
titleContent = NetworkStatusTitle(text: presentationData.strings.State_WaitingForNetwork, activity: true, hasProxy: false, connectsViaProxy: connectsViaProxy, isPasscodeSet: isRoot && isPasscodeSet, isManuallyLocked: isRoot && isManuallyLocked, peerStatus: peerStatus)
case let .connecting(proxy):
let text = presentationData.strings.State_Connecting
/*if let layout = strongSelf.validLayout, proxy != nil && layout.metrics.widthClass != .regular && layout.size.width > 320.0 {*/
//text = self.presentationData.strings.State_ConnectingToProxy
//}
if let proxy = proxy, proxy.hasConnectionIssues {
checkProxy = true
}

View file

@ -1949,7 +1949,21 @@ final class ChatListControllerNode: ASDisplayNode, ASGestureRecognizerDelegate {
//filter.insert(.excludeRecent)
}
let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, requestPeerType: nil, location: effectiveLocation, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in
var folder: (Int32, String)?
if let folders = self.controller?.tabContainerData?.0 {
switch self.effectiveContainerNode.currentItemFilter {
case .all:
break
case let .filter(id):
if let value = folders.first(where: { $0.id == .filter(id) }) {
if case let .filter(_, text, _) = value {
folder = (id, text.text)
}
}
}
}
let contentNode = ChatListSearchContainerNode(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filter: filter, requestPeerType: nil, location: effectiveLocation, folder: folder, displaySearchFilters: displaySearchFilters, hasDownloads: hasDownloads, initialFilter: initialFilter, openPeer: { [weak self] peer, _, threadId, dismissSearch in
self?.requestOpenPeerFromSearch?(peer, threadId, dismissSearch)
}, openDisabledPeer: { _, _, _ in
}, openRecentPeerOptions: { [weak self] peer in

View file

@ -26,7 +26,6 @@ import AppBundle
import GalleryData
import InstantPageUI
import ChatInterfaceState
import ShareController
import UndoUI
import TextFormat
import Postbox
@ -43,6 +42,7 @@ import ComponentDisplayAdapters
private enum ChatListTokenId: Int32 {
case archive
case folder
case forum
case filter
case peer
@ -102,6 +102,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private let peersFilter: ChatListNodePeersFilter
private let requestPeerType: [ReplyMarkupButtonRequestPeerType]?
private var location: ChatListControllerLocation
private var folder: (Int32, String)?
private let displaySearchFilters: Bool
private let hasDownloads: Bool
private var interaction: ChatListSearchInteraction?
@ -165,7 +166,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
private var recentAppsDisposable: Disposable?
private var refreshedGlobalPostSearchStateDisposable: Disposable?
public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, parentController: @escaping () -> ViewController?) {
public init(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, requestPeerType: [ReplyMarkupButtonRequestPeerType]?, location: ChatListControllerLocation, folder: (Int32, String)?, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Int64?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer, Int64?, ChatListDisabledPeerReason) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, Int64?, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?, parentController: @escaping () -> ViewController?) {
var initialFilter = initialFilter
if case .chats = initialFilter, case .forum = location {
initialFilter = .topics
@ -175,6 +176,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.peersFilter = filter
self.requestPeerType = requestPeerType
self.location = location
self.folder = folder
self.displaySearchFilters = displaySearchFilters
self.hasDownloads = hasDownloads
self.navigationController = navigationController
@ -194,6 +198,11 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
self.paneContainerNode.clipsToBounds = true
super.init()
if let folder {
self.searchOptionsValue = self.currentSearchOptions.withUpdatedFolder(folder)
self.searchOptions.set(.single(self.currentSearchOptions))
}
self.backgroundColor = filter.contains(.excludeRecent) ? nil : self.presentationData.theme.chatList.backgroundColor
@ -588,7 +597,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
private var currentSearchOptions: ChatListSearchOptions {
return self.searchOptionsValue ?? ChatListSearchOptions(peer: nil, date: nil)
return self.searchOptionsValue ?? ChatListSearchOptions(peer: nil, date: nil, folder: nil)
}
public override func searchTokensUpdated(tokens: [SearchBarToken]) {
@ -609,12 +618,19 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
if !tokensIdSet.contains(ChatListTokenId.peer.rawValue) && updatedOptions?.peer != nil {
updatedOptions = updatedOptions?.withUpdatedPeer(nil)
}
if !tokensIdSet.contains(ChatListTokenId.folder.rawValue) && updatedOptions?.folder != nil {
updatedOptions = updatedOptions?.withUpdatedFolder(nil)
self.folder = nil
}
self.updateSearchOptions(updatedOptions)
}
private func updateSearchOptions(_ options: ChatListSearchOptions?, clearQuery: Bool = false) {
var options = options
var tokens: [SearchBarToken] = []
if let folder = self.folder {
tokens.append(SearchBarToken(id: ChatListTokenId.folder.rawValue, icon: nil, iconOffset: 0.0, peer: nil, title: folder.1, permanent: false))
}
if case .chatList(.archive) = self.location {
tokens.append(SearchBarToken(id: ChatListTokenId.archive.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/Archive"), iconOffset: -1.0, title: self.presentationData.strings.ChatList_Archive, permanent: false))
} else if case .forum = self.location, let forumPeer = self.forumPeer {
@ -737,10 +753,13 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
key = .chats
}
self.paneContainerNode.requestSelectPane(key)
self.updateSearchOptions(nil)
self.updateSearchOptions(self.currentSearchOptions)
self.searchTextUpdated(text: query ?? "")
var tokens: [SearchBarToken] = []
if let folder = self.folder {
tokens.append(SearchBarToken(id: ChatListTokenId.folder.rawValue, icon: nil, iconOffset: 0.0, peer: nil, title: folder.1, permanent: false))
}
if case .chatList(.archive) = self.location {
tokens.append(SearchBarToken(id: ChatListTokenId.archive.rawValue, icon: UIImage(bundleImageName: "Chat List/Search/Archive"), iconOffset: -1.0, title: self.presentationData.strings.ChatList_Archive, permanent: false))
} else if case .forum = self.location, let forumPeer = self.forumPeer {
@ -809,6 +828,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
transition.updateFrame(node: self.filterContainerNode, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left + filtersInsets.left, y: layout.size.height - filtersInsets.bottom - 40.0), size: CGSize(width: layout.size.width - (layout.safeInsets.left + filtersInsets.left) * 2.0, height: 40.0)))
self.updateFilterContainerNode(layout: layout, transition: transition)
self.filterContainerNode.isHidden = !self.displaySearchFilters
if isFirstTime {
self.filterContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.appearanceTimestamp = CACurrentMediaTime()
@ -855,9 +876,9 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
|> deliverOnMainQueue).startStandalone(next: { messages in
if let strongSelf = self, !messages.isEmpty {
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages.sorted(by: { lhs, rhs in
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .messages(messages.sorted(by: { lhs, rhs in
return lhs.index < rhs.index
}).map({ $0._asMessage() })), externalShare: true, immediateExternalShare: true)
}).map({ $0._asMessage() })), externalShare: true, immediateExternalShare: true))
strongSelf.dismissInput()
strongSelf.present?(shareController, nil)
}
@ -1322,8 +1343,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
}
public override func searchTextClearTokens() {
self.folder = nil
self.updateSearchOptions(nil)
// self.setQuery?(nil, [], self.searchQueryValue ?? "")
}
func deleteMessages(messageIds: Set<EngineMessage.Id>?) {

View file

@ -1448,17 +1448,22 @@ public enum ChatListSearchContextActionSource {
public struct ChatListSearchOptions {
let peer: (EnginePeer.Id, Bool, String)?
let date: (Int32?, Int32, String)?
let folder: (Int32, String)?
var isEmpty: Bool {
return self.peer == nil && self.date == nil
return self.peer == nil && self.date == nil && self.folder == nil
}
func withUpdatedPeer(_ peerIdIsGroupAndName: (EnginePeer.Id, Bool, String)?) -> ChatListSearchOptions {
return ChatListSearchOptions(peer: peerIdIsGroupAndName, date: self.date)
return ChatListSearchOptions(peer: peerIdIsGroupAndName, date: self.date, folder: self.folder)
}
func withUpdatedDate(_ minDateMaxDateAndTitle: (Int32?, Int32, String)?) -> ChatListSearchOptions {
return ChatListSearchOptions(peer: self.peer, date: minDateMaxDateAndTitle)
return ChatListSearchOptions(peer: self.peer, date: minDateMaxDateAndTitle, folder: self.folder)
}
func withUpdatedFolder(_ folder: (Int32, String)?) -> ChatListSearchOptions {
return ChatListSearchOptions(peer: self.peer, date: self.date, folder: folder)
}
}
@ -2065,7 +2070,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
var defaultFoundRemoteMessagesSignal: Signal<([FoundRemoteMessages], Bool), NoError> = .single(([FoundRemoteMessages(messages: [], readCounters: [:], threadsData: [:], totalCount: 0)], false))
if key == .globalPosts, let data = context.currentAppConfiguration.with({ $0 }).data, let value = data["ios_load_empty_global_posts"] as? Double, value != 0.0 {
let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: nil), tags: nil, minDate: nil, maxDate: nil), query: "", state: nil, limit: 50)
let searchSignal = context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: nil), tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: "", state: nil, limit: 50)
|> map { resultData -> ChatListSearchMessagesResult in
let (result, updatedState) = resultData
@ -2089,7 +2094,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let foundItems: Signal<([ChatListSearchEntry], Bool, String?)?, NoError> = combineLatest(queue: .mainQueue(), searchQuery, self.approvedGlobalPostQueryState.get(), searchOptions, self.searchScopePromise.get(), downloadItems, globalPostSearchStateType, isPremium)
|> debounceOnMainThread
|> mapToSignal { [weak self] query, approvedGlobalPostQueryState, options, searchScope, downloadItems, _, _ -> Signal<([ChatListSearchEntry], Bool, String?)?, NoError> in
if query == nil && options == nil && [.chats, .topics, .channels, .apps].contains(key) {
if query == nil && (options == nil || options?.withUpdatedFolder(nil).isEmpty == true) && [.chats, .topics, .channels, .apps].contains(key) {
let _ = currentRemotePeers.swap(nil)
return .single(nil)
}
@ -2244,7 +2249,24 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
return result
}
let updatedLocalPeers = context.engine.contacts.searchLocalPeers(query: query.lowercased())
var predicate: Signal<ChatListFilterPredicate?, NoError> = .single(nil)
if let folderId = options?.folder?.0 {
predicate = context.engine.peers.currentChatListFilters()
|> take(1)
|> map { filters -> ChatListFilterPredicate? in
guard let filter = filters.first(where: { $0.id == folderId }) else {
return nil
}
guard case let .filter(_, _, _, data) = filter else {
return nil
}
return chatListFilterPredicate(filter: data, accountPeerId: context.account.peerId)
}
}
let updatedLocalPeers = predicate |> mapToSignal { predicate in
return context.engine.contacts.searchLocalPeers(query: query.lowercased(), predicate: predicate)
}
|> mapToSignal { peers -> Signal<[EngineRenderedPeer], NoError> in
return context.engine.data.subscribe(
EngineDataMap(peers.map { peer in
@ -2552,7 +2574,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
if case .savedMessagesChats = location {
foundRemotePeers = .single(([], [], [], false))
} else if let query = query, case .chats = key {
if query.hasPrefix("#") {
if query.hasPrefix("#") || options?.folder != nil {
foundRemotePeers = .single(([], [], [], false))
} else {
foundRemotePeers = (
@ -2584,28 +2606,28 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
}
let searchLocations: [SearchMessagesLocation]
if key == .globalPosts {
searchLocations = [SearchMessagesLocation.general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil)]
} else if let options = options {
searchLocations = [SearchMessagesLocation.general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil, folderId: nil)]
} else if let options {
if case let .forum(peerId) = location {
searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(scope: .everywhere, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)]
searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1), .general(scope: .everywhere, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1, folderId: nil)]
} else if let (peerId, _, _) = options.peer {
searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: options.date?.0, maxDate: options.date?.1)]
} else {
if case let .chatList(groupId) = location, case .archive = groupId {
searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)]
} else {
searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1)]
searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: options.date?.0, maxDate: options.date?.1, folderId: options.folder?.0)]
}
}
} else {
if case .channels = key {
searchLocations = [.general(scope: .channels, tags: tagMask, minDate: nil, maxDate: nil)]
searchLocations = [.general(scope: .channels, tags: tagMask, minDate: nil, maxDate: nil, folderId: nil)]
} else if case let .forum(peerId) = location {
searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: nil, maxDate: nil), .general(scope: .everywhere, tags: tagMask, minDate: nil, maxDate: nil)]
searchLocations = [.peer(peerId: peerId, fromId: nil, tags: tagMask, reactions: nil, threadId: nil, minDate: nil, maxDate: nil), .general(scope: .everywhere, tags: tagMask, minDate: nil, maxDate: nil, folderId: nil)]
} else if case let .chatList(groupId) = location, case .archive = groupId {
searchLocations = [.group(groupId: groupId._asGroup(), tags: tagMask, minDate: nil, maxDate: nil)]
} else {
searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: nil, maxDate: nil)]
searchLocations = [.general(scope: searchScope, tags: tagMask, minDate: nil, maxDate: nil, folderId: nil)]
}
}
@ -2704,7 +2726,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
let searchSignals: [Signal<(SearchMessagesResult, SearchMessagesState), NoError>]
if key == .globalPosts {
searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil), query: finalQuery, state: nil, limit: 50)]
searchSignals = [context.engine.messages.searchMessages(location: .general(scope: .globalPosts(allowPaidStars: approvedGlobalPostQueryState?.price), tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: finalQuery, state: nil, limit: 50)]
} else {
searchSignals = searchLocations.map { searchLocation in
return context.engine.messages.searchMessages(location: searchLocation, query: finalQuery, state: nil, limit: 50)

View file

@ -1309,7 +1309,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
var item: ChatListItem?
public private(set) var item: ChatListItem?
private let backgroundNode: ASDisplayNode
private let highlightedBackgroundNode: ASDisplayNode
@ -2722,7 +2722,8 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
let messageString: NSAttributedString
if !messageText.isEmpty && entities.count > 0 {
messageString = foldLineBreaks(stringWithAppliedEntities(messageText, entities: entities, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage()))
let appliedString = stringWithAppliedEntities(messageText, entities: entities, strings: item.presentationData.strings, dateTimeFormat: item.presentationData.dateTimeFormat, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message._asMessage())
messageString = foldLineBreaks(appliedString)
} else if spoilers != nil || customEmojiRanges != nil {
let mutableString = NSMutableAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor)
if let spoilers = spoilers {

View file

@ -287,7 +287,9 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
}
if !processed {
if !message.text.isEmpty {
messageText = "📎 \(messageText)"
if enableMediaEmoji {
messageText = "📎 \(messageText)"
}
} else {
if fileMedia.isAnimatedSticker {
messageText = strings.Message_Sticker

View file

@ -27,7 +27,7 @@ import GlobalControlPanelsContext
public enum ChatListNodeMode {
case chatList(appendContacts: Bool)
case peers(filter: ChatListNodePeersFilter, isSelecting: Bool, additionalCategories: [ChatListNodeAdditionalCategory], chatListFilters: [ChatListFilter]?, displayAutoremoveTimeout: Bool, displayPresence: Bool)
case peers(filter: ChatListNodePeersFilter, isSelecting: Bool, additionalCategories: [ChatListNodeAdditionalCategory], topPeers: [EnginePeer], chatListFilters: [ChatListFilter]?, displayAutoremoveTimeout: Bool, displayPresence: Bool)
case peerType(type: [ReplyMarkupButtonRequestPeerType], hasCreate: Bool)
}
@ -372,286 +372,268 @@ public struct ChatListNodeState: Equatable {
}
}
private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, isPremium: Bool, filterData: ChatListItemFilterData?, chatListFilters: [ChatListFilter]?, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)?, entries: [ChatListNodeViewTransitionInsertEntry]) -> [ListViewInsertItem] {
private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, isPremium: Bool, filterData: ChatListItemFilterData?, chatListFilters: [ChatListFilter]?, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)?, entries: [ChatListNodeViewTransitionInsertEntry], presentationData: ChatListPresentationData) -> [ListViewInsertItem] {
return entries.map { entry -> ListViewInsertItem in
switch entry.entry {
case .HeaderEntry:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
case let .AdditionalCategory(_, id, title, image, appearance, selected, presentationData):
var header: ChatListSearchItemHeader?
if case .peerType = mode {
} else {
if case .action = appearance {
// TODO: hack, generalize
header = ChatListSearchItemHeader(type: .orImportIntoAnExistingGroup, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
}
case .HeaderEntry:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
case let .AdditionalCategory(_, id, title, image, appearance, selected, presentationData):
var header: ChatListSearchItemHeader?
if case .peerType = mode {
} else {
if case .action = appearance {
// TODO: hack, generalize
header = ChatListSearchItemHeader(type: .orImportIntoAnExistingGroup, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListAdditionalCategoryItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
context: context,
title: title,
image: image,
appearance: appearance,
isSelected: selected,
header: header,
action: {
nodeInteraction.additionalCategorySelected(id)
}
), directionHint: entry.directionHint)
case let .PeerEntry(peerEntry):
let index = peerEntry.index
let presentationData = peerEntry.presentationData
let combinedReadState = peerEntry.readState
let isRemovedFromTotalUnreadCount = peerEntry.isRemovedFromTotalUnreadCount
let draftState = peerEntry.draftState
let peer = peerEntry.peer
let threadInfo = peerEntry.threadInfo
let presence = peerEntry.presence
let hasUnseenMentions = peerEntry.hasUnseenMentions
let hasUnseenReactions = peerEntry.hasUnseenReactions
let hasUnseenPollVotes = peerEntry.hasUnseenPollVotes
let editing = peerEntry.editing
let hasActiveRevealControls = peerEntry.hasActiveRevealControls
let selected = peerEntry.selected
let inputActivities = peerEntry.inputActivities
let promoInfo = peerEntry.promoInfo
let hasFailedMessages = peerEntry.hasFailedMessages
let isContact = peerEntry.isContact
let forumTopicData = peerEntry.forumTopicData
let topForumTopicItems = peerEntry.topForumTopicItems
let revealed = peerEntry.revealed
switch mode {
case .chatList:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: presentationData,
context: context,
chatListLocation: location,
filterData: filterData,
index: index,
content: .peer(ChatListItemContent.PeerData(
messages: peerEntry.messages,
peer: peer,
threadInfo: threadInfo,
combinedReadState: combinedReadState,
isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount,
presence: presence,
hasUnseenMentions: hasUnseenMentions,
hasUnseenReactions: hasUnseenReactions,
hasUnseenPollVotes: hasUnseenPollVotes,
draftState: draftState,
mediaDraftContentType: peerEntry.mediaDraftContentType,
inputActivities: inputActivities,
promoInfo: promoInfo,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: hasFailedMessages,
forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems,
autoremoveTimeout: peerEntry.autoremoveTimeout,
storyState: peerEntry.storyState.flatMap { storyState in
return ChatListItemContent.StoryState(
stats: storyState.stats,
hasUnseenCloseFriends: storyState.hasUnseenCloseFriends
)
},
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
displayAsTopicList: peerEntry.displayAsTopicList,
tags: chatListItemTags(location: location, accountPeerId: context.account.peerId, isPremium: isPremium, peer: peer.chatMainPeer, isUnread: combinedReadState?.isUnread ?? false, isMuted: isRemovedFromTotalUnreadCount, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: chatListFilters)
)),
editing: editing,
hasActiveRevealControls: hasActiveRevealControls,
selected: selected,
header: nil,
enabledContextActions: .auto,
hiddenOffset: threadInfo?.isHidden == true && !revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters, displayAutoremoveTimeout, displayPresence):
let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] {
chatPeer = peer
}
var enabled = true
if let isPeerEnabled {
if let itemPeer {
enabled = isPeerEnabled(itemPeer)
}
} else {
if filter.contains(.onlyWriteable) {
if let peer = peer.peers[peer.peerId] {
if !canSendMessagesToPeer(peer._asPeer()) {
enabled = false
}
if peerEntry.requiresPremiumForMessaging {
enabled = false
}
} else {
enabled = false
}
if let threadInfo, threadInfo.isClosed, case let .channel(channel) = itemPeer {
if threadInfo.isOwnedByMe || channel.hasPermission(.manageTopics) {
} else {
enabled = false
}
}
}
if filter.contains(.onlyPrivateChats) {
if let peer = peer.peers[peer.peerId] {
switch peer {
case .user, .secretChat:
break
default:
enabled = false
}
} else {
enabled = false
}
}
if filter.contains(.onlyGroups) {
if let peer = peer.peers[peer.peerId] {
if case .legacyGroup = peer {
} else if case let .channel(peer) = peer, case .group = peer.info {
} else {
enabled = false
}
} else {
enabled = false
}
}
if filter.contains(.onlyManageable) {
if let peer = peer.peers[peer.peerId] {
var canManage = false
if case let .legacyGroup(peer) = peer {
switch peer.role {
case .creator, .admin:
canManage = true
default:
break
}
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
canManage = true
} else if case let .channel(peer) = peer, case .broadcast = peer.info, peer.hasPermission(.addAdmins) {
canManage = true
}
if canManage {
} else {
enabled = false
}
} else {
enabled = false
}
}
if filter.contains(.excludeChannels) {
if let peer = peer.peers[peer.peerId] {
if case let .channel(peer) = peer, case .broadcast = peer.info {
enabled = false
}
}
}
}
var header: ChatListSearchItemHeader?
switch mode {
case let .peers(_, _, additionalCategories, _, _, _):
if !additionalCategories.isEmpty {
let headerType: ChatListSearchItemHeaderType
if case .action = additionalCategories[0].appearance {
// TODO: hack, generalize
headerType = .orImportIntoAnExistingGroup
} else {
headerType = .chats
}
header = ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
}
default:
break
}
var status: ContactsPeerItemStatus = .none
if isSelecting, let itemPeer = itemPeer {
if displayPresence, let presence = presence {
status = .presence(presence, presentationData.dateTimeFormat)
} else if let (string, multiline, isActive, icon) = statusStringForPeerType(accountPeerId: context.account.peerId, strings: presentationData.strings, peer: itemPeer, isMuted: isRemovedFromTotalUnreadCount, isUnread: combinedReadState?.isUnread ?? false, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: filters, displayAutoremoveTimeout: displayAutoremoveTimeout, autoremoveTimeout: peerEntry.autoremoveTimeout) {
status = .custom(string: string, multiline: multiline, isActive: isActive, icon: icon)
} else {
status = .none
}
}
let peerContent: ContactsPeerItemPeer
if let threadInfo = threadInfo, let itemPeer = itemPeer {
peerContent = .thread(peer: itemPeer, title: threadInfo.info.title, icon: threadInfo.info.icon, color: threadInfo.info.iconColor)
} else {
peerContent = .peer(peer: itemPeer, chatPeer: chatPeer)
}
var threadId: Int64?
switch index {
case let .forum(_, _, threadIdValue, _, _):
threadId = threadIdValue
case .chatList:
break
}
var isForum = false
if let peer = chatPeer, case let .channel(channel) = peer, channel.isForumOrMonoForum {
isForum = true
if editing, case .chatList = mode {
enabled = false
}
}
var selectable = editing
if case .chatList = mode {
if isForum {
selectable = false
}
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
sortOrder: presentationData.nameSortOrder,
displayOrder: presentationData.nameDisplayOrder,
context: context,
peerMode: .generalSearch(isSavedMessages: false),
peer: peerContent,
status: status,
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
enabled: enabled,
selection: selectable ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,
action: { _ in
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer, threadId)
} else {
nodeInteraction.peerSelected(chatPeer, nil, threadId, nil, false)
}
}
}, disabledAction: (isForum && editing) && !peerEntry.requiresPremiumForMessaging ? nil : { _ in
if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer, threadId, peerEntry.requiresPremiumForMessaging ? .premiumRequired : .generic)
}
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListAdditionalCategoryItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
context: context,
title: title,
image: image,
appearance: appearance,
isSelected: selected,
header: header,
action: {
nodeInteraction.additionalCategorySelected(id)
}
), directionHint: entry.directionHint)
case let .TopPeer(_, peer):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
sortOrder: presentationData.nameSortOrder,
displayOrder: presentationData.nameDisplayOrder,
context: context,
peerMode: .generalSearch(isSavedMessages: false),
peer: .peer(peer: peer, chatPeer: peer),
status: .none,
requiresPremiumForMessaging: false,
enabled: true,
selection: .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: nil,
action: { _ in
nodeInteraction.peerSelected(peer, nil, nil, nil, false)
},
disabledAction: nil,
animationCache: nodeInteraction.animationCache,
animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint)
case let .PeerEntry(peerEntry):
let index = peerEntry.index
let presentationData = peerEntry.presentationData
let combinedReadState = peerEntry.readState
let isRemovedFromTotalUnreadCount = peerEntry.isRemovedFromTotalUnreadCount
let draftState = peerEntry.draftState
let peer = peerEntry.peer
let threadInfo = peerEntry.threadInfo
let presence = peerEntry.presence
let hasUnseenMentions = peerEntry.hasUnseenMentions
let hasUnseenReactions = peerEntry.hasUnseenReactions
let hasUnseenPollVotes = peerEntry.hasUnseenPollVotes
let editing = peerEntry.editing
let hasActiveRevealControls = peerEntry.hasActiveRevealControls
let selected = peerEntry.selected
let inputActivities = peerEntry.inputActivities
let promoInfo = peerEntry.promoInfo
let hasFailedMessages = peerEntry.hasFailedMessages
let isContact = peerEntry.isContact
let forumTopicData = peerEntry.forumTopicData
let topForumTopicItems = peerEntry.topForumTopicItems
let revealed = peerEntry.revealed
switch mode {
case .chatList:
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: presentationData,
context: context,
chatListLocation: location,
filterData: filterData,
index: index,
content: .peer(ChatListItemContent.PeerData(
messages: peerEntry.messages,
peer: peer,
threadInfo: threadInfo,
combinedReadState: combinedReadState,
isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount,
presence: presence,
hasUnseenMentions: hasUnseenMentions,
hasUnseenReactions: hasUnseenReactions,
hasUnseenPollVotes: hasUnseenPollVotes,
draftState: draftState,
mediaDraftContentType: peerEntry.mediaDraftContentType,
inputActivities: inputActivities,
promoInfo: promoInfo,
ignoreUnreadBadge: false,
displayAsMessage: false,
hasFailedMessages: hasFailedMessages,
forumTopicData: forumTopicData,
topForumTopicItems: topForumTopicItems,
autoremoveTimeout: peerEntry.autoremoveTimeout,
storyState: peerEntry.storyState.flatMap { storyState in
return ChatListItemContent.StoryState(
stats: storyState.stats,
hasUnseenCloseFriends: storyState.hasUnseenCloseFriends
)
},
animationCache: nodeInteraction.animationCache,
animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint)
case .peerType:
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
displayAsTopicList: peerEntry.displayAsTopicList,
tags: chatListItemTags(location: location, accountPeerId: context.account.peerId, isPremium: isPremium, peer: peer.chatMainPeer, isUnread: combinedReadState?.isUnread ?? false, isMuted: isRemovedFromTotalUnreadCount, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: chatListFilters)
)),
editing: editing,
hasActiveRevealControls: hasActiveRevealControls,
selected: selected,
header: nil,
enabledContextActions: .auto,
hiddenOffset: threadInfo?.isHidden == true && !revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, _, filters, displayAutoremoveTimeout, displayPresence):
let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] {
chatPeer = peer
}
var enabled = true
if let isPeerEnabled {
if let itemPeer {
enabled = isPeerEnabled(itemPeer)
}
} else {
if filter.contains(.onlyWriteable) {
if let peer = peer.peers[peer.peerId] {
if !canSendMessagesToPeer(peer._asPeer()) {
enabled = false
}
if peerEntry.requiresPremiumForMessaging {
enabled = false
}
} else {
enabled = false
}
if let threadInfo, threadInfo.isClosed, case let .channel(channel) = itemPeer {
if threadInfo.isOwnedByMe || channel.hasPermission(.manageTopics) {
} else {
enabled = false
}
}
}
if filter.contains(.onlyPrivateChats) {
if let peer = peer.peers[peer.peerId] {
switch peer {
case .user, .secretChat:
break
default:
enabled = false
}
} else {
enabled = false
}
}
if filter.contains(.onlyGroups) {
if let peer = peer.peers[peer.peerId] {
if case .legacyGroup = peer {
} else if case let .channel(peer) = peer, case .group = peer.info {
} else {
enabled = false
}
} else {
enabled = false
}
}
if filter.contains(.onlyManageable) {
if let peer = peer.peers[peer.peerId] {
var canManage = false
if case let .legacyGroup(peer) = peer {
switch peer.role {
case .creator, .admin:
canManage = true
default:
break
}
} else if case let .channel(peer) = peer, case .group = peer.info, peer.hasPermission(.inviteMembers) {
canManage = true
} else if case let .channel(peer) = peer, case .broadcast = peer.info, peer.hasPermission(.addAdmins) {
canManage = true
}
if canManage {
} else {
enabled = false
}
} else {
enabled = false
}
}
if filter.contains(.excludeChannels) {
if let peer = peer.peers[peer.peerId] {
if case let .channel(peer) = peer, case .broadcast = peer.info {
enabled = false
}
}
}
}
let peerContent: ContactsPeerItemPeer = .peer(peer: itemPeer, chatPeer: chatPeer)
let status: ContactsPeerItemStatus = .none
var header: ChatListSearchItemHeader?
switch mode {
case let .peers(_, _, additionalCategories, _, _, _, _):
if !additionalCategories.isEmpty {
let headerType: ChatListSearchItemHeaderType
if case .action = additionalCategories[0].appearance {
// TODO: hack, generalize
headerType = .orImportIntoAnExistingGroup
} else {
headerType = .chats
}
header = ChatListSearchItemHeader(type: headerType, theme: presentationData.theme, strings: presentationData.strings, actionTitle: nil, action: nil)
}
default:
break
}
var status: ContactsPeerItemStatus = .none
if isSelecting, let itemPeer = itemPeer {
if displayPresence, let presence = presence {
status = .presence(presence, presentationData.dateTimeFormat)
} else if let (string, multiline, isActive, icon) = statusStringForPeerType(accountPeerId: context.account.peerId, strings: presentationData.strings, peer: itemPeer, isMuted: isRemovedFromTotalUnreadCount, isUnread: combinedReadState?.isUnread ?? false, isContact: isContact, hasUnseenMentions: hasUnseenMentions, chatListFilters: filters, displayAutoremoveTimeout: displayAutoremoveTimeout, autoremoveTimeout: peerEntry.autoremoveTimeout) {
status = .custom(string: string, multiline: multiline, isActive: isActive, icon: icon)
} else {
status = .none
}
}
let peerContent: ContactsPeerItemPeer
if let threadInfo = threadInfo, let itemPeer = itemPeer {
peerContent = .thread(peer: itemPeer, title: threadInfo.info.title, icon: threadInfo.info.icon, color: threadInfo.info.iconColor)
} else {
peerContent = .peer(peer: itemPeer, chatPeer: chatPeer)
}
var threadId: Int64?
switch index {
case let .forum(_, _, threadIdValue, _, _):
threadId = threadIdValue
case .chatList:
break
}
var isForum = false
if let peer = chatPeer, case let .channel(channel) = peer, channel.isForumOrMonoForum {
isForum = true
if editing, case .chatList = mode {
enabled = false
}
}
var selectable = editing
if case .chatList = mode {
if isForum {
selectable = false
}
}
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
sortOrder: presentationData.nameSortOrder,
@ -661,69 +643,37 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
peer: peerContent,
status: status,
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
enabled: true,
selection: editing ? .selectable(selected: selected) : .none,
enabled: enabled,
selection: selectable ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: nil,
header: header,
action: { _ in
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer, nil)
nodeInteraction.togglePeerSelected(chatPeer, threadId)
} else {
nodeInteraction.peerSelected(chatPeer, nil, nil, nil, false)
nodeInteraction.peerSelected(chatPeer, nil, threadId, nil, false)
}
}
}, disabledAction: peerEntry.requiresPremiumForMessaging ? { _ in
if let chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer, nil, .premiumRequired)
}, disabledAction: (isForum && editing) && !peerEntry.requiresPremiumForMessaging ? nil : { _ in
if let chatPeer = chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer, threadId, peerEntry.requiresPremiumForMessaging ? .premiumRequired : .generic)
}
} : nil,
},
animationCache: nodeInteraction.animationCache,
animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint)
case .peerType:
let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] {
chatPeer = peer
}
case let .HoleEntry(_, theme):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
case let .GroupReferenceEntry(groupReferenceEntry):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: groupReferenceEntry.presentationData,
context: context,
chatListLocation: location,
filterData: filterData,
index: groupReferenceEntry.index,
content: .groupReference(ChatListItemContent.GroupReferenceData(
groupId: groupReferenceEntry.groupId,
peers: groupReferenceEntry.peers,
message: groupReferenceEntry.message,
unreadCount: groupReferenceEntry.unreadCount,
hiddenByDefault: groupReferenceEntry.hiddenByDefault,
appearsPinned: groupReferenceEntry.appearsPinned,
storyState: groupReferenceEntry.storyState.flatMap { storyState in
return ChatListItemContent.StoryState(
stats: storyState.stats,
hasUnseenCloseFriends: storyState.hasUnseenCloseFriends
)
}
)),
editing: groupReferenceEntry.editing,
hasActiveRevealControls: false,
selected: false,
header: nil,
enabledContextActions: .auto,
hiddenOffset: groupReferenceEntry.hiddenByDefault && !groupReferenceEntry.revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .ContactEntry(contactEntry):
let header: ChatListSearchItemHeader? = nil
var status: ContactsPeerItemStatus = .none
status = .presence(contactEntry.presence, contactEntry.presentationData.dateTimeFormat)
let presentationData = contactEntry.presentationData
let peerContent: ContactsPeerItemPeer = .peer(peer: contactEntry.peer, chatPeer: contactEntry.peer)
let peerContent: ContactsPeerItemPeer = .peer(peer: itemPeer, chatPeer: chatPeer)
let status: ContactsPeerItemStatus = .none
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
sortOrder: presentationData.nameSortOrder,
@ -732,31 +682,103 @@ private func mappedInsertEntries(context: AccountContext, nodeInteraction: ChatL
peerMode: .generalSearch(isSavedMessages: false),
peer: peerContent,
status: status,
requiresPremiumForMessaging: peerEntry.requiresPremiumForMessaging,
enabled: true,
selection: .none,
selection: editing ? .selectable(selected: selected) : .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,
header: nil,
action: { _ in
nodeInteraction.peerSelected(contactEntry.peer, nil, nil, nil, false)
},
disabledAction: nil,
if let chatPeer = chatPeer {
if editing {
nodeInteraction.togglePeerSelected(chatPeer, nil)
} else {
nodeInteraction.peerSelected(chatPeer, nil, nil, nil, false)
}
}
}, disabledAction: peerEntry.requiresPremiumForMessaging ? { _ in
if let chatPeer {
nodeInteraction.disabledPeerSelected(chatPeer, nil, .premiumRequired)
}
} : nil,
animationCache: nodeInteraction.animationCache,
animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .EmptyIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .SectionHeader(presentationData, displayHide):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSectionHeaderItem(theme: presentationData.theme, strings: presentationData.strings, hide: displayHide ? {
hideChatListContacts(context: context)
} : nil), directionHint: entry.directionHint)
}
case let .HoleEntry(_, theme):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListHoleItem(theme: theme), directionHint: entry.directionHint)
case let .GroupReferenceEntry(groupReferenceEntry):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListItem(
presentationData: groupReferenceEntry.presentationData,
context: context,
chatListLocation: location,
filterData: filterData,
index: groupReferenceEntry.index,
content: .groupReference(ChatListItemContent.GroupReferenceData(
groupId: groupReferenceEntry.groupId,
peers: groupReferenceEntry.peers,
message: groupReferenceEntry.message,
unreadCount: groupReferenceEntry.unreadCount,
hiddenByDefault: groupReferenceEntry.hiddenByDefault,
appearsPinned: groupReferenceEntry.appearsPinned,
storyState: groupReferenceEntry.storyState.flatMap { storyState in
return ChatListItemContent.StoryState(
stats: storyState.stats,
hasUnseenCloseFriends: storyState.hasUnseenCloseFriends
)
}
)),
editing: groupReferenceEntry.editing,
hasActiveRevealControls: false,
selected: false,
header: nil,
enabledContextActions: .auto,
hiddenOffset: groupReferenceEntry.hiddenByDefault && !groupReferenceEntry.revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .ContactEntry(contactEntry):
let header: ChatListSearchItemHeader? = nil
var status: ContactsPeerItemStatus = .none
status = .presence(contactEntry.presence, contactEntry.presentationData.dateTimeFormat)
let presentationData = contactEntry.presentationData
let peerContent: ContactsPeerItemPeer = .peer(peer: contactEntry.peer, chatPeer: contactEntry.peer)
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
sortOrder: presentationData.nameSortOrder,
displayOrder: presentationData.nameDisplayOrder,
context: context,
peerMode: .generalSearch(isSavedMessages: false),
peer: peerContent,
status: status,
enabled: true,
selection: .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: header,
action: { _ in
nodeInteraction.peerSelected(contactEntry.peer, nil, nil, nil, false)
},
disabledAction: nil,
animationCache: nodeInteraction.animationCache,
animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint)
case let .ArchiveIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListArchiveInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .EmptyIntro(presentationData):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyInfoItem(theme: presentationData.theme, strings: presentationData.strings), directionHint: entry.directionHint)
case let .SectionHeader(presentationData, displayHide):
return ListViewInsertItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListSectionHeaderItem(theme: presentationData.theme, strings: presentationData.strings, hide: displayHide ? {
hideChatListContacts(context: context)
} : nil), directionHint: entry.directionHint)
}
}
}
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, isPremium: Bool, filterData: ChatListItemFilterData?, chatListFilters: [ChatListFilter]?, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)?, entries: [ChatListNodeViewTransitionUpdateEntry]) -> [ListViewUpdateItem] {
private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, isPremium: Bool, filterData: ChatListItemFilterData?, chatListFilters: [ChatListFilter]?, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)?, entries: [ChatListNodeViewTransitionUpdateEntry], presentationData: ChatListPresentationData) -> [ListViewUpdateItem] {
return entries.map { entry -> ListViewUpdateItem in
switch entry.entry {
case let .PeerEntry(peerEntry):
@ -828,7 +850,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
hiddenOffset: threadInfo?.isHidden == true && !revealed,
interaction: nodeInteraction
), directionHint: entry.directionHint)
case let .peers(filter, isSelecting, _, filters, displayAutoremoveTimeout, displayPresence):
case let .peers(filter, isSelecting, _, _, filters, displayAutoremoveTimeout, displayPresence):
let itemPeer = peer.chatOrMonoforumMainPeer
var chatPeer: EnginePeer?
if let peer = peer.peers[peer.peerId] {
@ -868,7 +890,7 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
var header: ChatListSearchItemHeader?
switch mode {
case let .peers(_, _, additionalCategories, _, _, _):
case let .peers(_, _, additionalCategories, _, _, _, _):
if !additionalCategories.isEmpty {
let headerType: ChatListSearchItemHeaderType
if case .action = additionalCategories[0].appearance {
@ -1066,6 +1088,28 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
} : nil), directionHint: entry.directionHint)
case .HeaderEntry:
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ChatListEmptyHeaderItem(), directionHint: entry.directionHint)
case let .TopPeer(_, peer):
return ListViewUpdateItem(index: entry.index, previousIndex: entry.previousIndex, item: ContactsPeerItem(
presentationData: ItemListPresentationData(theme: presentationData.theme, fontSize: presentationData.fontSize, strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, dateTimeFormat: presentationData.dateTimeFormat),
sortOrder: presentationData.nameSortOrder,
displayOrder: presentationData.nameDisplayOrder,
context: context,
peerMode: .generalSearch(isSavedMessages: false),
peer: .peer(peer: peer, chatPeer: peer),
status: .none,
requiresPremiumForMessaging: false,
enabled: true,
selection: .none,
editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false),
index: nil,
header: nil,
action: { _ in
nodeInteraction.peerSelected(peer, nil, nil, nil, false)
},
disabledAction: nil,
animationCache: nodeInteraction.animationCache,
animationRenderer: nodeInteraction.animationRenderer
), directionHint: entry.directionHint)
case let .AdditionalCategory(index: _, id, title, image, appearance, selected, presentationData):
var header: ChatListSearchItemHeader?
if case .peerType = mode {
@ -1091,8 +1135,8 @@ private func mappedUpdateEntries(context: AccountContext, nodeInteraction: ChatL
}
}
private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, isPremium: Bool, filterData: ChatListItemFilterData?, chatListFilters: [ChatListFilter]?, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)?, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition {
return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.insertEntries), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.updateEntries), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem, animateCrossfade: transition.animateCrossfade)
private func mappedChatListNodeViewListTransition(context: AccountContext, nodeInteraction: ChatListNodeInteraction, location: ChatListControllerLocation, isPremium: Bool, filterData: ChatListItemFilterData?, chatListFilters: [ChatListFilter]?, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)?, presentationData: ChatListPresentationData, transition: ChatListNodeViewTransition) -> ChatListNodeListViewTransition {
return ChatListNodeListViewTransition(chatListView: transition.chatListView, deleteItems: transition.deleteItems, insertItems: mappedInsertEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.insertEntries, presentationData: presentationData), updateItems: mappedUpdateEntries(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: isPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, entries: transition.updateEntries, presentationData: presentationData), options: transition.options, scrollToItem: transition.scrollToItem, stationaryItemRange: transition.stationaryItemRange, adjustScrollToFirstItem: transition.adjustScrollToFirstItem, animateCrossfade: transition.animateCrossfade)
}
private final class ChatListOpaqueTransactionState {
@ -1196,7 +1240,7 @@ public final class ChatListNode: ListViewImpl {
return nil
case let .PeerId(value):
return PeerId(value)
case .ThreadId, .GroupId, .ContactId, .ArchiveIntro, .EmptyIntro, .SectionHeader, .Notice, .additionalCategory:
case .ThreadId, .GroupId, .ContactId, .ArchiveIntro, .EmptyIntro, .SectionHeader, .Notice, .additionalCategory, .TopPeer:
return nil
}
}
@ -1326,7 +1370,7 @@ public final class ChatListNode: ListViewImpl {
}
var isSelecting = false
if case .peers(_, true, _, _, _, _) = mode {
if case .peers(_, true, _, _, _, _, _) = mode {
isSelecting = true
}
@ -1839,7 +1883,7 @@ public final class ChatListNode: ListViewImpl {
let currentRemovingItemId = self.currentRemovingItemId
let savedMessagesPeer: Signal<EnginePeer?, NoError>
if case let .peers(filter, _, _, _, _, _) = mode, filter.contains(.onlyWriteable), case .chatList = location, self.chatListFilter == nil {
if case let .peers(filter, _, _, _, _, _, _) = mode, filter.contains(.onlyWriteable), case .chatList = location, self.chatListFilter == nil {
savedMessagesPeer = context.account.postbox.loadedPeerWithId(context.account.peerId)
|> map(Optional.init)
|> map { peer in
@ -2102,7 +2146,7 @@ public final class ChatListNode: ListViewImpl {
case .chatList:
isEmpty = false
return true
case let .peers(filter, _, _, _, _, _):
case let .peers(filter, _, _, _, _, _, _):
guard !filter.contains(.excludeSavedMessages) || peer.peerId != currentPeerId else { return false }
guard !filter.contains(.excludeSavedMessages) || !peer.peerId.isReplies else { return false }
guard !filter.contains(.excludeSecretChats) || peer.peerId.namespace != Namespaces.Peer.SecretChat else { return false }
@ -2538,9 +2582,10 @@ public final class ChatListNode: ListViewImpl {
if accountIsPremium != previousAccountIsPremium.swap(accountIsPremium) {
forceAllUpdated = true
}
let presentationData = state.presentationData
return preparedChatListNodeViewTransition(from: previousView, to: processedView, reason: reason, previewing: previewing, disableAnimations: disableAnimations, account: context.account, scrollPosition: updatedScrollPosition, searchMode: searchMode, forceAllUpdated: forceAllUpdated)
|> map({ mappedChatListNodeViewListTransition(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: accountIsPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, transition: $0) })
|> map({ mappedChatListNodeViewListTransition(context: context, nodeInteraction: nodeInteraction, location: location, isPremium: accountIsPremium, filterData: filterData, chatListFilters: chatListFilters, mode: mode, isPeerEnabled: isPeerEnabled, presentationData: presentationData, transition: $0) })
|> runOn(prepareOnMainQueue ? Queue.mainQueue() : viewProcessingQueue)
}
@ -3391,7 +3436,7 @@ public final class ChatListNode: ListViewImpl {
} else {
break loop
}
case .ArchiveIntro, .EmptyIntro, .SectionHeader, .HeaderEntry, .AdditionalCategory:
case .ArchiveIntro, .EmptyIntro, .SectionHeader, .HeaderEntry, .AdditionalCategory, .TopPeer:
break
}
}

View file

@ -18,6 +18,7 @@ enum ChatListNodeEntryId: Hashable {
case SectionHeader
case Notice
case additionalCategory(Int)
case TopPeer(EnginePeer.Id)
}
enum ChatListNodeEntrySortIndex: Comparable {
@ -25,6 +26,7 @@ enum ChatListNodeEntrySortIndex: Comparable {
case additionalCategory(Int)
case sectionHeader
case contact(id: EnginePeer.Id, presence: EnginePeer.Presence)
case topPeer(Int)
static func <(lhs: ChatListNodeEntrySortIndex, rhs: ChatListNodeEntrySortIndex) -> Bool {
switch lhs {
@ -34,6 +36,8 @@ enum ChatListNodeEntrySortIndex: Comparable {
return lhsIndex < rhsIndex
case .additionalCategory:
return false
case .topPeer:
return false
case .sectionHeader:
return true
case .contact:
@ -43,6 +47,8 @@ enum ChatListNodeEntrySortIndex: Comparable {
switch rhs {
case let .additionalCategory(rhsIndex):
return lhsIndex < rhsIndex
case .topPeer:
return true
case .index:
return true
case .sectionHeader:
@ -50,9 +56,18 @@ enum ChatListNodeEntrySortIndex: Comparable {
case .contact:
return true
}
case let .topPeer(lhsIndex):
switch rhs {
case .additionalCategory:
return false
case let .topPeer(rhsIndex):
return lhsIndex < rhsIndex
case .index, .sectionHeader, .contact:
return true
}
case .sectionHeader:
switch rhs {
case .additionalCategory, .index, .sectionHeader:
case .additionalCategory, .topPeer, .index, .sectionHeader:
return false
case .contact:
return true
@ -405,6 +420,7 @@ enum ChatListNodeEntry: Comparable, Identifiable {
case EmptyIntro(presentationData: ChatListPresentationData)
case SectionHeader(presentationData: ChatListPresentationData, displayHide: Bool)
case AdditionalCategory(index: Int, id: Int, title: String, image: UIImage?, appearance: ChatListNodeAdditionalCategory.Appearance, selected: Bool, presentationData: ChatListPresentationData)
case TopPeer(index: Int, peer: EnginePeer)
var sortIndex: ChatListNodeEntrySortIndex {
switch self {
@ -426,6 +442,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .sectionHeader
case let .AdditionalCategory(index, _, _, _, _, _, _):
return .additionalCategory(index)
case let .TopPeer(index, _):
return .topPeer(index)
}
}
@ -454,6 +472,8 @@ enum ChatListNodeEntry: Comparable, Identifiable {
return .SectionHeader
case let .AdditionalCategory(_, id, _, _, _, _, _):
return .additionalCategory(id)
case let .TopPeer(_, peer):
return .TopPeer(peer.id)
}
}
@ -551,6 +571,12 @@ enum ChatListNodeEntry: Comparable, Identifiable {
} else {
return false
}
case let .TopPeer(index, peer):
if case .TopPeer(index, peer) = rhs {
return true
} else {
return false
}
}
}
}
@ -930,12 +956,18 @@ func chatListNodeEntriesForView(view: EngineChatList, state: ChatListNodeState,
}
if !view.hasLater {
if case let .peers(_, _, additionalCategories, _, _, _) = mode {
if case let .peers(_, _, additionalCategories, topPeers, _, _, _) = mode {
var index = 0
for category in additionalCategories.reversed() {
result.append(.AdditionalCategory(index: index, id: category.id, title: category.title, image: category.icon, appearance: category.appearance, selected: state.selectedAdditionalCategoryIds.contains(category.id), presentationData: state.presentationData))
index += 1
}
index = 0
for topPeer in topPeers {
result.append(.TopPeer(index: index, peer: topPeer))
index += 1
}
} else if case let .peerType(types, hasCreate) = mode, !result.isEmpty && hasCreate {
for type in types {
switch type {

View file

@ -503,6 +503,20 @@ public final class PagerComponent<ChildEnvironmentType: Equatable, TopPanelEnvir
self.component?.contentIdUpdated(id)
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.alpha.isZero {
return nil
}
for view in self.subviews.reversed() {
if let result = view.hitTest(self.convert(point, to: view), with: event), result.isUserInteractionEnabled {
return result
}
}
let result = super.hitTest(point, with: event)
return result
}
func update(component: PagerComponent<ChildEnvironmentType, TopPanelEnvironment>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
let previousPanelHideBehavior = self.component?.panelHideBehavior

View file

@ -365,7 +365,7 @@ public class ContactsController: ViewController {
self.contactsNode.requestAddContact = { [weak self] phoneNumber in
if let strongSelf = self {
strongSelf.view.endEditing(true)
strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in
strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, peer: nil, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in
self?.present(controller, in: .window(.root), with: arguments)
}, pushController: { [weak self] controller in
(self?.navigationController as? NavigationController)?.pushViewController(controller)
@ -757,6 +757,8 @@ public class ContactsController: ViewController {
let controller = strongSelf.context.sharedContext.makeNewContactScreen(
context: strongSelf.context,
peer: nil,
firstName: nil,
lastName: nil,
phoneNumber: nil,
shareViaException: false,
completion: { [weak self] peer, stableId, contactData in

View file

@ -28,10 +28,12 @@ public final class NavigationBarTheme {
public let badgeStrokeColor: UIColor
public let badgeTextColor: UIColor
public let edgeEffectColor: UIColor?
public let accentButtonColor: UIColor
public let accentForegroundColor: UIColor
public let style: NavigationBar.Style
public let glassStyle: NavigationBar.GlassStyle
public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, style: NavigationBar.Style = .legacy, glassStyle: NavigationBar.GlassStyle = .default) {
public init(overallDarkAppearance: Bool, buttonColor: UIColor, disabledButtonColor: UIColor, primaryTextColor: UIColor, backgroundColor: UIColor, opaqueBackgroundColor: UIColor? = nil, enableBackgroundBlur: Bool, separatorColor: UIColor, badgeBackgroundColor: UIColor, badgeStrokeColor: UIColor, badgeTextColor: UIColor, edgeEffectColor: UIColor? = nil, accentButtonColor: UIColor, accentForegroundColor: UIColor, style: NavigationBar.Style = .legacy, glassStyle: NavigationBar.GlassStyle = .default) {
self.overallDarkAppearance = overallDarkAppearance
self.buttonColor = buttonColor
self.disabledButtonColor = disabledButtonColor
@ -44,16 +46,18 @@ public final class NavigationBarTheme {
self.badgeStrokeColor = badgeStrokeColor
self.badgeTextColor = badgeTextColor
self.edgeEffectColor = edgeEffectColor
self.accentButtonColor = accentButtonColor
self.accentForegroundColor = accentForegroundColor
self.style = style
self.glassStyle = glassStyle
}
public func withUpdatedBackgroundColor(_ color: UIColor) -> NavigationBarTheme {
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style, glassStyle: self.glassStyle)
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: color, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: false, separatorColor: self.separatorColor, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle)
}
public func withUpdatedSeparatorColor(_ color: UIColor) -> NavigationBarTheme {
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, style: self.style, glassStyle: self.glassStyle)
return NavigationBarTheme(overallDarkAppearance: self.overallDarkAppearance, buttonColor: self.buttonColor, disabledButtonColor: self.disabledButtonColor, primaryTextColor: self.primaryTextColor, backgroundColor: self.backgroundColor, opaqueBackgroundColor: self.opaqueBackgroundColor, enableBackgroundBlur: self.enableBackgroundBlur, separatorColor: color, badgeBackgroundColor: self.badgeBackgroundColor, badgeStrokeColor: self.badgeStrokeColor, badgeTextColor: self.badgeTextColor, edgeEffectColor: self.edgeEffectColor, accentButtonColor: self.accentButtonColor, accentForegroundColor: self.accentForegroundColor, style: self.style, glassStyle: self.glassStyle)
}
}

View file

@ -1216,11 +1216,11 @@ open class TextNode: ASDisplayNode, TextNodeProtocol {
public static let all: RenderContentTypes = [.text, .emoji]
}
final class DrawingParameters: NSObject {
public final class DrawingParameters: NSObject {
let cachedLayout: TextNodeLayout?
let renderContentTypes: RenderContentTypes
init(cachedLayout: TextNodeLayout?, renderContentTypes: RenderContentTypes) {
public init(cachedLayout: TextNodeLayout?, renderContentTypes: RenderContentTypes) {
self.cachedLayout = cachedLayout
self.renderContentTypes = renderContentTypes
@ -1295,7 +1295,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol {
}
}
private static func calculateLayoutV2(
public static func calculateLayoutV2(
attributedString: NSAttributedString,
minimumNumberOfLines: Int,
maximumNumberOfLines: Int,
@ -1685,7 +1685,7 @@ open class TextNode: ASDisplayNode, TextNodeProtocol {
)
}
static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textShadowBlur: CGFloat?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool, displayEmbeddedItemsUnderSpoilers: Bool, customTruncationToken: NSAttributedString?) -> TextNodeLayout {
public static func calculateLayout(attributedString: NSAttributedString?, minimumNumberOfLines: Int, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, backgroundColor: UIColor?, constrainedSize: CGSize, alignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacingFactor: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, lineColor: UIColor?, textShadowColor: UIColor?, textShadowBlur: CGFloat?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool, displayEmbeddedItemsUnderSpoilers: Bool, customTruncationToken: NSAttributedString?) -> TextNodeLayout {
guard let attributedString else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textShadowBlur: textShadowBlur, textStroke: textStroke, displaySpoilers: displaySpoilers)
}

View file

@ -12,7 +12,6 @@ import TextFormat
import TelegramStringFormatting
import AccountContext
import RadialStatusNode
import ShareController
import OpenInExternalAppUI
import AppBundle
import LocalizedPeerData
@ -311,8 +310,20 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
}
}
didSet {
if let scrubberView = self.scrubberView {
if let scrubberView = self.scrubberView {
self.view.addSubview(scrubberView)
scrubberView.onRequestLayout = { [weak self] transition in
guard let self else {
return
}
if let requestLayout = self.requestLayout {
requestLayout(transition)
} else {
if let validLayout = self.validLayout {
let _ = self.updateLayout(size: validLayout.0, metrics: validLayout.1, leftInset: validLayout.2, rightInset: validLayout.3, bottomInset: validLayout.4, contentInset: validLayout.5, transition: transition)
}
}
}
scrubberView.updateScrubbingVisual = { [weak self] value in
guard let strongSelf = self else {
return
@ -489,8 +500,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
let theme = defaultDarkPresentationTheme
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (self.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), self.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) })
let shareController = ShareController(context: self.context, subject: .text(text.string), externalShare: true, immediateExternalShare: false, updatedPresentationData: updatedPresentationData)
let shareController = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .text(text.string), externalShare: true, immediateExternalShare: false, updatedPresentationData: updatedPresentationData))
self.controllerInteraction?.presentController(shareController, nil)
case .lookup:
let controller = UIReferenceLibraryViewController(term: text.string)
@ -1297,7 +1308,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
panelHeight -= 44.0
}
let scrubberFrame = CGRect(origin: CGPoint(x: buttonPanelInsets.left, y: scrubberY), size: CGSize(width: width - buttonPanelInsets.left - buttonPanelInsets.right, height: 44.0))
var scrubberFrame = CGRect(origin: CGPoint(x: buttonPanelInsets.left, y: scrubberY), size: CGSize(width: width - buttonPanelInsets.left - buttonPanelInsets.right, height: 44.0))
if scrubberView.hasVisibleInfo {
let infoHeight: CGFloat = 16.0
scrubberFrame.size.height += infoHeight
panelHeight += infoHeight
}
scrubberView.updateLayout(size: scrubberFrame.size, leftInset: 0.0, rightInset: 0.0, isCollapsed: self.visibilityAlpha < 1.0, transition: transition)
transition.updateBounds(layer: scrubberView.layer, bounds: CGRect(origin: CGPoint(), size: scrubberFrame.size))
transition.updatePosition(layer: scrubberView.layer, position: CGPoint(x: scrubberFrame.midX, y: scrubberFrame.midY))
@ -1888,41 +1905,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
}
}
let shareController = ShareController(context: strongSelf.context, subject: subject, preferredAction: preferredAction, externalShare: hasExternalShare, forceTheme: forceTheme)
shareController.dismissed = { [weak self] _ in
self?.interacting?(false)
}
shareController.onMediaTimestampLinkCopied = { [weak self] timestamp in
guard let self else {
return
}
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let text: String
if let timestamp {
let startTimeString: String
let hours = timestamp / (60 * 60)
let minutes = timestamp % (60 * 60) / 60
let seconds = timestamp % 60
if hours != 0 {
startTimeString = String(format: "%d:%02d:%02d", hours, minutes, seconds)
} else {
startTimeString = String(format: "%d:%02d", minutes, seconds)
}
text = presentationData.strings.Conversation_VideoTimeLinkCopied(startTimeString).string
} else {
text = presentationData.strings.Conversation_LinkCopied
}
self.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: text), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
}
shareController.actionCompleted = { [weak self] in
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: subject, preferredAction: preferredAction, externalShare: hasExternalShare, forceTheme: forceTheme, actionCompleted: { [weak self] in
if let strongSelf = self, let actionCompletionText = actionCompletionText {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: actionCompletionText), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
}
}
shareController.completed = { [weak self] peerIds in
}, dismissed: { [weak self] _ in
self?.interacting?(false)
}, completed: { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.engine.data.get(
EngineDataList(
@ -1933,7 +1923,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
@ -1958,12 +1948,33 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
text = ""
}
}
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
}
})
}
}
}, onMediaTimestampLinkCopied: { [weak self] timestamp in
guard let self else {
return
}
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let text: String
if let timestamp {
let startTimeString: String
let hours = timestamp / (60 * 60)
let minutes = timestamp % (60 * 60) / 60
let seconds = timestamp % 60
if hours != 0 {
startTimeString = String(format: "%d:%02d:%02d", hours, minutes, seconds)
} else {
startTimeString = String(format: "%d:%02d", minutes, seconds)
}
text = presentationData.strings.Conversation_VideoTimeLinkCopied(startTimeString).string
} else {
text = presentationData.strings.Conversation_LinkCopied
}
self.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: text), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
}))
strongSelf.controllerInteraction?.presentController(shareController, nil)
} else {
var singleText = presentationData.strings.Media_ShareItem(1)
@ -1984,17 +1995,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
let shareAction: ([Message]) -> Void = { messages in
if let strongSelf = self {
let shareController = ShareController(context: strongSelf.context, subject: .messages(messages), preferredAction: preferredAction, forceTheme: forceTheme)
shareController.dismissed = { [weak self] _ in
self?.interacting?(false)
}
shareController.actionCompleted = { [weak self, weak shareController] in
if let strongSelf = self, let shareController = shareController, shareController.actionIsMediaSaving {
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .messages(messages), preferredAction: preferredAction, forceTheme: forceTheme, actionCompleted: { [weak self] in
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: presentationData.strings.Gallery_ImageSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
}
}
shareController.completed = { [weak self] peerIds in
}, dismissed: { [weak self] _ in
self?.interacting?(false)
}, completed: { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.engine.data.get(
EngineDataList(
@ -2005,7 +2013,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
@ -2030,12 +2038,12 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
text = ""
}
}
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
}
})
}
}
}))
strongSelf.controllerInteraction?.presentController(shareController, nil)
}
}
@ -2142,11 +2150,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
}
}
}
let shareController = ShareController(context: self.context, subject: subject, preferredAction: preferredAction, forceTheme: forceTheme)
shareController.dismissed = { [weak self] _ in
let shareController = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: subject, preferredAction: preferredAction, forceTheme: forceTheme, dismissed: { [weak self] _ in
self?.interacting?(false)
}
shareController.completed = { [weak self] peerIds in
}, completed: { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.engine.data.get(
EngineDataList(
@ -2157,7 +2163,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
@ -2182,12 +2188,12 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
text = ""
}
}
strongSelf.controllerInteraction?.presentController(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: true, animateInAsReplacement: true, action: { _ in return false }), nil)
}
})
}
}
}))
self.controllerInteraction?.presentController(shareController, nil)
}
}

View file

@ -38,13 +38,11 @@ final class ChatVideoGalleryItemScrubberView: UIView {
private var chapters: [MediaPlayerScrubbingChapter] = []
private var fetchStatusDisposable = MetaDisposable()
private var scrubbingDisposable = MetaDisposable()
private var chapterDisposable = MetaDisposable()
private var loadingDisposable = MetaDisposable()
private var leftTimestampNodePushed = false
private var rightTimestampNodePushed = false
private var infoNodePushed = false
private var currentChapter: MediaPlayerScrubbingChapter?
@ -53,6 +51,14 @@ final class ChatVideoGalleryItemScrubberView: UIView {
private var currentLeftString: String?
private var currentRightString: String?
var hasVisibleInfo: Bool {
if let attributedText = self.infoNode.attributedText, !attributedText.string.isEmpty {
return true
} else {
return false
}
}
var hideWhenDurationIsUnknown = false {
didSet {
if self.hideWhenDurationIsUnknown {
@ -72,6 +78,8 @@ final class ChatVideoGalleryItemScrubberView: UIView {
var updateScrubbingHandlePosition: (CGFloat) -> Void = { _ in }
var seek: (Double) -> Void = { _ in }
var onRequestLayout: ((ContainedViewLayoutTransition) -> Void)?
init(chapters: [MediaPlayerScrubbingChapter]) {
self.backgroundContainer = GlassBackgroundContainerView()
self.backgroundView = GlassBackgroundView()
@ -131,7 +139,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
self.backgroundView.contentView.addSubview(self.leftTimestampNode.view)
self.backgroundView.contentView.addSubview(self.rightTimestampNode.view)
self.addSubview(self.scrubberNode.view)
//self.backgroundView.contentView.addSubview(self.infoNode.view)
self.backgroundView.contentView.addSubview(self.infoNode.view)
}
required init?(coder aDecoder: NSCoder) {
@ -139,7 +147,6 @@ final class ChatVideoGalleryItemScrubberView: UIView {
}
deinit {
self.scrubbingDisposable.dispose()
self.fetchStatusDisposable.dispose()
self.chapterDisposable.dispose()
self.loadingDisposable.dispose()
@ -148,6 +155,16 @@ final class ChatVideoGalleryItemScrubberView: UIView {
var isLoading = false
var isCollapsed: Bool?
private func requestLayout(transition: ContainedViewLayoutTransition) {
if let onRequestLayout = self.onRequestLayout {
onRequestLayout(transition)
} else {
if let (size, leftInset, rightInset, isCollapsed) = self.containerLayout {
self.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isCollapsed: isCollapsed, transition: .immediate)
}
}
}
func updateTimestampsVisibility(animated: Bool) {
if self.isAnimatedOut {
return
@ -276,41 +293,11 @@ final class ChatVideoGalleryItemScrubberView: UIView {
}
strongSelf.infoNode.attributedText = NSAttributedString(string: chapter.title, font: textFont, textColor: .white)
if let (size, leftInset, rightInset, isCollapsed) = strongSelf.containerLayout {
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isCollapsed: isCollapsed, transition: .immediate)
}
strongSelf.requestLayout(transition: .immediate)
}
}
}))
}
self.scrubbingDisposable.set((self.scrubberNode.scrubbingPosition
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
}
let leftTimestampNodePushed: Bool = false
let rightTimestampNodePushed: Bool = false
let infoNodePushed: Bool
if let value = value {
//leftTimestampNodePushed = value < 0.16
//rightTimestampNodePushed = value > 0.84
infoNodePushed = value >= 0.16 && value <= 0.84
} else {
//leftTimestampNodePushed = false
//rightTimestampNodePushed = false
infoNodePushed = false
}
if leftTimestampNodePushed != strongSelf.leftTimestampNodePushed || rightTimestampNodePushed != strongSelf.rightTimestampNodePushed || infoNodePushed != strongSelf.infoNodePushed {
strongSelf.leftTimestampNodePushed = leftTimestampNodePushed
strongSelf.rightTimestampNodePushed = rightTimestampNodePushed
strongSelf.infoNodePushed = infoNodePushed
if let layout = strongSelf.containerLayout {
strongSelf.updateLayout(size: layout.0, leftInset: layout.1, rightInset: layout.2, isCollapsed: layout.3, transition: .animated(duration: 0.35, curve: .spring))
}
}
}))
}
func setBufferingStatusSignal(_ status: Signal<(RangeSet<Int64>, Int64)?, NoError>?) {
@ -335,16 +322,16 @@ final class ChatVideoGalleryItemScrubberView: UIView {
}
strongSelf.infoNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: .white)
if let (size, leftInset, rightInset, isCollapsed) = strongSelf.containerLayout {
strongSelf.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, isCollapsed: isCollapsed, transition: .immediate)
}
strongSelf.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
}
}))
} else if self.chapters.isEmpty {
self.infoNode.attributedText = NSAttributedString(string: dataSizeString(fileSize, forceDecimal: true, formatting: formatting), font: textFont, textColor: .white)
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
}
} else if self.chapters.isEmpty {
self.infoNode.attributedText = nil
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
}
}
@ -376,27 +363,32 @@ final class ChatVideoGalleryItemScrubberView: UIView {
}
}
var yOffset: CGFloat = 0.0
if !isCollapsed {
yOffset = size.height - 44.0
}
leftTimestampOffset = 14.0
rightTimestampOffset = 14.0
infoOffset = 0.0
infoOffset = -8.0
if isCollapsed {
scrubberLeftInset = 0.0
scrubberRightInset = 0.0
}
transition.setFrame(view: self.leftTimestampNode.view, frame: CGRect(origin: CGPoint(x: 16.0, y: leftTimestampOffset), size: CGSize(width: 60.0, height: 20.0)))
transition.setFrame(view: self.rightTimestampNode.view, frame: CGRect(origin: CGPoint(x: size.width - leftInset - rightInset - 60.0 - 16.0, y: rightTimestampOffset), size: CGSize(width: 60.0, height: 20.0)))
transition.setFrame(view: self.leftTimestampNode.view, frame: CGRect(origin: CGPoint(x: 16.0, y: yOffset + leftTimestampOffset), size: CGSize(width: 60.0, height: 20.0)))
transition.setFrame(view: self.rightTimestampNode.view, frame: CGRect(origin: CGPoint(x: size.width - leftInset - rightInset - 60.0 - 16.0, y: yOffset + rightTimestampOffset), size: CGSize(width: 60.0, height: 20.0)))
var infoConstrainedSize = size
infoConstrainedSize.width = size.width - scrubberLeftInset - scrubberRightInset - 100.0
infoConstrainedSize.width = size.width - 20.0 * 2.0
let infoSize = self.infoNode.measure(infoConstrainedSize)
self.infoNode.bounds = CGRect(origin: CGPoint(), size: infoSize)
transition.setPosition(view: self.infoNode.view, position: CGPoint(x: size.width / 2.0, y: infoOffset + infoSize.height / 2.0))
self.infoNode.alpha = size.width < size.height && self.isCollapsed == false ? 1.0 : 0.0
transition.setPosition(view: self.infoNode.view, position: CGPoint(x: size.width / 2.0, y: yOffset + infoOffset + infoSize.height / 2.0))
self.infoNode.alpha = self.isCollapsed == false ? 1.0 : 0.0
var scrubberFrame = CGRect(origin: CGPoint(x: scrubberLeftInset, y: 15.0), size: CGSize(width: size.width - leftInset - rightInset - scrubberLeftInset - scrubberRightInset, height: scrubberHeight))
var scrubberFrame = CGRect(origin: CGPoint(x: scrubberLeftInset, y: yOffset + 15.0), size: CGSize(width: size.width - leftInset - rightInset - scrubberLeftInset - scrubberRightInset, height: scrubberHeight))
if isCollapsed {
scrubberFrame.origin.y = size.height - scrubberHeight
}

View file

@ -690,7 +690,7 @@ private func galleryEntriesForMessageHistoryEntries(_ entries: [MessageHistoryEn
}
public class GalleryController: ViewController, StandalonePresentableController, KeyShortcutResponder, GalleryControllerProtocol {
public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, edgeEffectColor: .clear, style: .glass)
public static let darkNavigationTheme = NavigationBarTheme(overallDarkAppearance: true, buttonColor: .white, disabledButtonColor: UIColor(rgb: 0x525252), primaryTextColor: .white, backgroundColor: UIColor(white: 0.0, alpha: 0.6), enableBackgroundBlur: false, separatorColor: UIColor(white: 0.0, alpha: 0.8), badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, edgeEffectColor: .clear, accentButtonColor: .white, accentForegroundColor: .black, style: .glass)
private var galleryNode: GalleryControllerNode {
return self.displayNode as! GalleryControllerNode
@ -927,8 +927,9 @@ public class GalleryController: ViewController, StandalonePresentableController,
if case .custom = source {
displayInfoOnTop = true
}
let syncResult = Atomic<(Bool, (() -> Void)?)>(value: (false, nil))
var isFirstTime = true
self.disposable.set(combineLatest(
messageView,
self.context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration]) |> take(1),
@ -1015,14 +1016,22 @@ public class GalleryController: ViewController, StandalonePresentableController,
storeMediaPlaybackState: strongSelf.actionInteraction?.storeMediaPlaybackState ?? { _, _, _ in },
generateStoreAfterDownload: strongSelf.generateStoreAfterDownload,
sendSticker: strongSelf.actionInteraction?.sendSticker,
present: { [weak self] c, a in
if let strongSelf = self {
present: { [weak strongSelf] c, a in
if let strongSelf {
strongSelf.presentInGlobalOverlay(c, with: a)
}
}
) {
if isCentral {
centralItemIndex = items.count
if isFirstTime {
isFirstTime = false
if item is UniversalVideoGalleryItem {
} else {
strongSelf.galleryNode.areControlsHidden = false
}
}
}
items.append(item)
}

View file

@ -9,7 +9,6 @@ import TelegramCore
import TelegramPresentationData
import AccountContext
import RadialStatusNode
import ShareController
class ChatExternalFileGalleryItem: GalleryItem {
var id: AnyHashable {
@ -333,7 +332,7 @@ class ChatExternalFileGalleryItemNode: GalleryItemNode {
@objc func actionButtonPressed() {
if let (context, _) = self.contextAndFile, let message = self.message, let status = self.status, case .Local = status {
let baseNavigationController = self.baseNavigationController()
(baseNavigationController?.topViewController as? ViewController)?.present(ShareController(context: context, subject: .messages([message]), showInChat: nil, externalShare: true, immediateExternalShare: true), in: .window(.root))
(baseNavigationController?.topViewController as? ViewController)?.present(context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .messages([message]), showInChat: nil, externalShare: true, immediateExternalShare: true)), in: .window(.root))
}
}
}

View file

@ -17,7 +17,6 @@ import ImageContentAnalysis
import TextSelectionNode
import Speak
import TranslateUI
import ShareController
import UndoUI
import ContextUI
import SaveToCameraRoll
@ -388,7 +387,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
}
case .share:
if let controller = strongSelf.baseNavigationController()?.topViewController as? ViewController {
let shareController = ShareController(context: strongSelf.context, subject: .text(string), externalShare: true, immediateExternalShare: false, updatedPresentationData: (strongSelf.context.sharedContext.currentPresentationData.with({ $0 }), strongSelf.context.sharedContext.presentationData))
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .text(string), externalShare: true, immediateExternalShare: false, updatedPresentationData: (strongSelf.context.sharedContext.currentPresentationData.with({ $0 }), strongSelf.context.sharedContext.presentationData)))
controller.present(shareController, in: .window(.root))
}
case .lookup:
@ -755,7 +754,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Gallery_SaveImage, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in
f(.default)
let _ = (SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: media)
|> deliverOnMainQueue).start(completed: { [weak self] in
guard let strongSelf = self else {

View file

@ -3857,10 +3857,14 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
if let (message, _, _) = strongSelf.contentInfo(), let image = message.media.first(where: { $0 is TelegramMediaImage }) as? TelegramMediaImage, !message.isCopyProtected() && !item.peerIsCopyProtected && message.paidContent == nil {
let context = strongSelf.context
var videoReference: AnyMediaReference?
if let video = image.video {
videoReference = .message(message: MessageReference(message), media: video)
}
items.append(.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Gallery_SaveImage, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Download"), color: theme.actionSheet.primaryTextColor) }, action: { [weak self] _, f in
f(.default)
let _ = (SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: .message(message: MessageReference(message), media: image))
let _ = (SaveToCameraRoll.saveToCameraRoll(context: context, postbox: context.account.postbox, userLocation: .peer(message.id.peerId), mediaReference: .message(message: MessageReference(message), media: image), video: videoReference)
|> deliverOnMainQueue).start(completed: { [weak self] in
guard let strongSelf = self else {
return
@ -3868,7 +3872,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
guard let controller = strongSelf.galleryController() else {
return
}
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: strongSelf.presentationData.strings.Gallery_ImageSaved), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .mediaSaved(text: strongSelf.presentationData.strings.Gallery_ImageSaved), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
})
})))
}

View file

@ -9,6 +9,7 @@ import TelegramPresentationData
import AccountContext
import ShareController
import UndoUI
import EmojiStatusComponent
private class WeakGameScriptMessageHandler: NSObject, WKScriptMessageHandler {
private let f: (WKScriptMessage) -> ()
@ -144,7 +145,7 @@ final class GameControllerNode: ViewControllerTracingNode {
if eventName == "share_game" || eventName == "share_score" {
if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty {
if eventName == "share_score" {
self.present(ShareController(context: self.context, subject: .fromExternal(1, { [weak self] peerIds, threadIds, requireStars, text, account, _ in
self.present(self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .fromExternal(1, { [weak self] peerIds, threadIds, requireStars, text, account, _ in
if let strongSelf = self, let message = strongSelf.message, let account = account as? ShareControllerAppAccountContext {
let signals = peerIds.map { TelegramEngine(account: account.context.account).messages.forwardGameWithScore(messageId: message.id, to: $0, threadId: threadIds[$0], as: nil) }
return .single(.preparing(false))
@ -158,7 +159,7 @@ final class GameControllerNode: ViewControllerTracingNode {
} else {
return .single(.done)
}
}), showInChat: nil, externalShare: false, immediateExternalShare: false), nil)
}), showInChat: nil, externalShare: false, immediateExternalShare: false)), nil)
} else {
self.shareWithoutScore()
}
@ -171,13 +172,12 @@ final class GameControllerNode: ViewControllerTracingNode {
let url = "https://t.me/\(addressName)?game=\(gameName)"
let context = self.context
let shareController = ShareController(context: context, subject: .url(url), showInChat: nil, externalShare: true)
shareController.actionCompleted = { [weak self] in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(url), showInChat: nil, externalShare: true, actionCompleted: { [weak self] in
if let strongSelf = self {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}
}))
self.present(shareController, nil)
}
}

View file

@ -54,7 +54,7 @@ final class HashtagSearchGlobalChatContents: ChatCustomContentsProtocol {
if self.publicPosts {
search = self.context.engine.messages.searchHashtagPosts(hashtag: self.query, state: nil)
} else {
search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil), query: self.query, state: nil)
search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: self.query, state: nil)
}
self.isSearchingPromise.set(true)
@ -102,7 +102,7 @@ final class HashtagSearchGlobalChatContents: ChatCustomContentsProtocol {
if self.publicPosts {
search = self.context.engine.messages.searchHashtagPosts(hashtag: self.query, state: self.currentSearchState)
} else {
search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil), query: self.query, state: currentSearchState)
search = self.context.engine.messages.searchMessages(location: .general(scope: .everywhere, tags: nil, minDate: nil, maxDate: nil, folderId: nil), query: self.query, state: currentSearchState)
}
self.historyViewDisposable?.dispose()

View file

@ -6,7 +6,6 @@ import TelegramCore
import SwiftSignalKit
import TelegramUIPreferences
import AccountContext
import ShareController
import StickerResources
import AlertUI
import PresentationDataUtils

View file

@ -9,7 +9,6 @@ import SafariServices
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import ShareController
import SaveToCameraRoll
import GalleryUI
import OpenInExternalAppUI
@ -142,14 +141,12 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
self.navigationBar.back = navigateBack
self.navigationBar.share = { [weak self] in
if let strongSelf = self, let (webPage, _) = strongSelf.webPage, case let .Loaded(content) = webPage.content {
let shareController = ShareController(context: context, subject: .url(content.url))
shareController.actionCompleted = { [weak self] in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(content.url), actionCompleted: { [weak self] in
if let strongSelf = self {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}
shareController.completed = { [weak self] peerIds in
}, completed: { [weak self] peerIds in
let _ = (context.engine.data.get(
EngineDataList(
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
@ -159,7 +156,7 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
@ -201,7 +198,7 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
}), nil)
}
})
}
}))
strongSelf.present(shareController, nil)
}
}
@ -1036,7 +1033,7 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
}
}), ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in
if let strongSelf = self, let (webPage, _) = strongSelf.webPage, case let .image(image) = media.media {
strongSelf.present(ShareController(context: strongSelf.context, subject: .image(image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.media(media: .webPage(webPage: WebpageReference(webPage), media: image), resource: $0.resource)) }))), nil)
strongSelf.present(strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .image(image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.media(media: .webPage(webPage: WebpageReference(webPage), media: image), resource: $0.resource)) })))), nil)
}
})], catchTapsOutside: true)
self.present(controller, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
@ -1150,7 +1147,7 @@ final class InstantPageControllerNode: ASDisplayNode, ASScrollViewDelegate {
}
}), ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuShare, accessibilityLabel: strings.Conversation_ContextMenuShare), action: { [weak self] in
if let strongSelf = self, let (webPage, _) = strongSelf.webPage, case let .Loaded(content) = webPage.content {
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
strongSelf.present(strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .quote(text: text, url: content.url))), nil)
}
})]

View file

@ -9,7 +9,6 @@ import Photos
import TelegramPresentationData
import TextFormat
import AccountContext
import ShareController
import GalleryUI
import AppBundle
@ -147,7 +146,7 @@ final class InstantPageGalleryFooterContentNode: GalleryFooterContentNode {
@objc func actionButtonPressed() {
if let shareMedia = self.shareMedia {
self.controllerInteraction?.presentController(ShareController(context: self.context, subject: .media(shareMedia, nil), preferredAction: .saveToCameraRoll, showInChat: nil, externalShare: true, immediateExternalShare: false), nil)
self.controllerInteraction?.presentController(self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: .media(shareMedia, nil), preferredAction: .saveToCameraRoll, showInChat: nil, externalShare: true, immediateExternalShare: false)), nil)
}
}
}

View file

@ -6,7 +6,6 @@ import TelegramCore
import SafariServices
import TelegramPresentationData
import AccountContext
import ShareController
import OpenInExternalAppUI
import TelegramUIPreferences
@ -414,7 +413,7 @@ class InstantPageReferenceControllerNode: ViewControllerTracingNode, ASScrollVie
UIPasteboard.general.string = text
}), ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuShare, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuShare), action: { [weak self] in
if let strongSelf = self, case let .Loaded(content) = strongSelf.webPage.webPage.content {
strongSelf.present(ShareController(context: strongSelf.context, subject: .quote(text: text, url: content.url)), nil)
strongSelf.present(strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .quote(text: text, url: content.url))), nil)
}
})])
controller.dismissed = { [weak self] in

View file

@ -17,7 +17,6 @@ import ContextUI
import TelegramStringFormatting
import ItemListPeerActionItem
import ItemListPeerItem
import ShareController
import UndoUI
import QrCodeUI
import PromptUI
@ -375,8 +374,10 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
}
let arguments = FolderInviteLinkListControllerArguments(context: context, shareMainLink: { inviteLink in
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
shareController.completed = { peerIds in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(inviteLink), updatedPresentationData: updatedPresentationData, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, completed: { peerIds in
let _ = (context.engine.data.get(
EngineDataList(
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
@ -385,7 +386,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
|> deliverOnMainQueue).start(next: { peerList in
let peers = peerList.compactMap { $0 }
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId {
@ -406,7 +407,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
text = ""
}
}
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if savedMessages, action == .info {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
@ -423,11 +424,7 @@ public func folderInviteLinkListController(context: AccountContext, updatedPrese
return false
}), nil)
})
}
shareController.actionCompleted = {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
}, openMainLink: { _ in
}, copyLink: { link in

View file

@ -14,7 +14,7 @@ import SectionHeaderItem
import TelegramStringFormatting
import MergeLists
import ContextUI
import ShareController
import OverlayStatusController
import PresentationDataUtils
import DirectionalPanGesture
@ -540,8 +540,12 @@ public final class InviteLinkInviteController: ViewController {
return
}
let updatedPresentationData = (strongSelf.presentationData, strongSelf.presentationDataPromise.get())
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
shareController.completed = { [weak self] peerIds in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(inviteLink), updatedPresentationData: updatedPresentationData, actionCompleted: { [weak self] in
if let strongSelf = self {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}, completed: { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.engine.data.get(
EngineDataList(
@ -552,7 +556,7 @@ public final class InviteLinkInviteController: ViewController {
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
@ -573,7 +577,7 @@ public final class InviteLinkInviteController: ViewController {
text = ""
}
}
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if savedMessages, let self, action == .info {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
@ -589,18 +593,12 @@ public final class InviteLinkInviteController: ViewController {
}
return false
}), in: .window(.root))
strongSelf.controller?.dismiss()
}
})
}
}
shareController.actionCompleted = { [weak self] in
if let strongSelf = self {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}
}))
strongSelf.controller?.present(shareController, in: .window(.root))
}, manageLinks: { [weak self] in
guard let strongSelf = self else {

View file

@ -17,7 +17,6 @@ import ContextUI
import TelegramStringFormatting
import ItemListPeerActionItem
import ItemListPeerItem
import ShareController
import UndoUI
import QrCodeUI
@ -433,8 +432,10 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
guard let inviteLink = invite.link else {
return
}
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
shareController.completed = { peerIds in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(inviteLink), updatedPresentationData: updatedPresentationData, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, completed: { peerIds in
let _ = (context.engine.data.get(
EngineDataList(
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
@ -443,7 +444,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|> deliverOnMainQueue).start(next: { peerList in
let peers = peerList.compactMap { $0 }
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId {
@ -464,7 +465,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
text = ""
}
}
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if savedMessages, action == .info {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
@ -481,11 +482,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
return false
}), nil)
})
}
shareController.actionCompleted = {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
}, openMainLink: { invite in
let controller = InviteLinkViewController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, invite: invite, invitationsContext: nil, revokedInvitationsContext: revokedInvitesContext, importersContext: nil)
@ -649,8 +646,10 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
return
}
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
shareController.completed = { peerIds in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(inviteLink), updatedPresentationData: updatedPresentationData, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}, completed: { peerIds in
let _ = (context.engine.data.get(
EngineDataList(
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
@ -659,7 +658,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
|> deliverOnMainQueue).start(next: { peerList in
let peers = peerList.compactMap { $0 }
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId {
@ -680,7 +679,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
text = ""
}
}
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if savedMessages, action == .info {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
@ -697,11 +696,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio
return false
}), nil)
})
}
shareController.actionCompleted = {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
})))

View file

@ -14,7 +14,7 @@ import SectionHeaderItem
import TelegramStringFormatting
import MergeLists
import ContextUI
import ShareController
import OverlayStatusController
import PresentationDataUtils
import DirectionalPanGesture
@ -610,8 +610,10 @@ public final class InviteLinkViewController: ViewController {
guard let inviteLink = invite.link else {
return
}
let shareController = ShareController(context: context, subject: .url(inviteLink))
shareController.completed = { [weak self] peerIds in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(inviteLink), actionCompleted: { [weak self] in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}, completed: { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.engine.data.get(
EngineDataList(
@ -622,7 +624,7 @@ public final class InviteLinkViewController: ViewController {
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == strongSelf.context.account.peerId {
@ -643,7 +645,7 @@ public final class InviteLinkViewController: ViewController {
text = ""
}
}
strongSelf.controller?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if savedMessages, let self, action == .info {
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
@ -662,11 +664,7 @@ public final class InviteLinkViewController: ViewController {
}
})
}
}
shareController.actionCompleted = { [weak self] in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}))
self?.controller?.present(shareController, in: .window(.root))
}, editLink: { [weak self] invite in
self?.editButtonPressed()

View file

@ -17,7 +17,6 @@ import ContextUI
import TelegramStringFormatting
import ItemListPeerActionItem
import ItemListPeerItem
import ShareController
import UndoUI
private final class InviteRequestsControllerArguments {

View file

@ -7,11 +7,11 @@ import TelegramPresentationData
import AvatarNode
import AccountContext
import SelectablePeerNode
import ShareController
import SolidRoundedButtonNode
import ActivityIndicator
import ComponentFlow
import EmojiStatusComponent
import ShareController
private let avatarFont = avatarPlaceholderFont(size: 42.0)

View file

@ -6,8 +6,8 @@ import TelegramCore
import TelegramPresentationData
import TextFormat
import AccountContext
import ShareController
import Markdown
import ShareController
final class LanguageLinkPreviewContentNode: ASDisplayNode, ShareContentContainerNode {
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?

View file

@ -291,9 +291,9 @@ typedef NS_ENUM(NSUInteger, FLAnimatedImageFrameCacheSize) {
// Calculate the optimal frame cache size: try choosing a larger buffer window depending on the predicted image size.
// It's only dependent on the image size & number of frames and never changes.
CGFloat animatedImageDataSize = CGImageGetBytesPerRow(self.posterImage.CGImage) * self.size.height * self.frameCount / MEGABYTE;
if (animatedImageDataSize <= FLAnimatedImageDataSizeCategoryAll) {
if (animatedImageDataSize <= (double)FLAnimatedImageDataSizeCategoryAll) {
_frameCacheSizeOptimal = self.frameCount;
} else if (animatedImageDataSize <= FLAnimatedImageDataSizeCategoryDefault) {
} else if (animatedImageDataSize <= (double)FLAnimatedImageDataSizeCategoryDefault) {
// This value doesn't depend on device memory much because if we're not keeping all frames in memory we will always be decoding 1 frame up ahead per 1 frame that gets played and at this point we might as well just keep a small buffer just large enough to keep from running out of frames.
_frameCacheSizeOptimal = FLAnimatedImageFrameCacheSizeDefault;
} else {

View file

@ -381,11 +381,11 @@ void dataProviderUnlockCallback (void *info, __unused const void *data, __unused
if ([GPUImageContext supportsFastTextureUpload])
{
cgImageFromBytes = CGImageCreate((int)_size.width, (int)_size.height, 8, 32, CVPixelBufferGetBytesPerRow(renderTarget), defaultRGBColorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst, dataProvider, NULL, NO, kCGRenderingIntentDefault);
cgImageFromBytes = CGImageCreate((int)_size.width, (int)_size.height, 8, 32, CVPixelBufferGetBytesPerRow(renderTarget), defaultRGBColorSpace, ((uint32_t)kCGBitmapByteOrder32Little) | ((uint32_t)kCGImageAlphaPremultipliedFirst), dataProvider, NULL, NO, kCGRenderingIntentDefault);
}
else
{
cgImageFromBytes = CGImageCreate((int)_size.width, (int)_size.height, 8, 32, 4 * (int)_size.width, defaultRGBColorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, dataProvider, NULL, NO, kCGRenderingIntentDefault);
cgImageFromBytes = CGImageCreate((int)_size.width, (int)_size.height, 8, 32, 4 * (int)_size.width, defaultRGBColorSpace, ((uint32_t)kCGBitmapByteOrderDefault) | ((uint32_t)kCGImageAlphaPremultipliedLast), dataProvider, NULL, NO, kCGRenderingIntentDefault);
}
// Capture image with current device orientation

View file

@ -150,7 +150,7 @@ NSString *const PGPhotoFilterMainShaderString = PGShaderString
CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();
CGContextRef imageContext = CGBitmapContextCreate(imageData, (size_t)imageSize.width, (size_t)imageSize.height, 8, (size_t)imageSize.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextRef imageContext = CGBitmapContextCreate(imageData, (size_t)imageSize.width, (size_t)imageSize.height, 8, (size_t)imageSize.width * 4, genericRGBColorspace, ((uint32_t)kCGBitmapByteOrder32Little) | ((uint32_t)kCGImageAlphaPremultipliedFirst));
CGSize imageDrawSize = CGSizeMake(imageSize.width, imageSize.height);
if (image.imageOrientation == UIImageOrientationLeft || image.imageOrientation == UIImageOrientationRight)

View file

@ -114,7 +114,7 @@
CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();
CGContextRef imageContext = CGBitmapContextCreate(imageData, (size_t)imageSize.width, (size_t)imageSize.height, 8, (size_t)imageSize.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextRef imageContext = CGBitmapContextCreate(imageData, (size_t)imageSize.width, (size_t)imageSize.height, 8, (size_t)imageSize.width * 4, genericRGBColorspace, ((uint32_t)kCGBitmapByteOrder32Little) | ((uint32_t)kCGImageAlphaPremultipliedFirst));
CGSize imageDrawSize = CGSizeMake(imageSize.width, imageSize.height);
if (orientation == UIImageOrientationLeft || orientation == UIImageOrientationRight)

View file

@ -492,7 +492,7 @@ static void addAttachmentImageCorners(void *memory, const unsigned int width, co
defaultAlphaMemory = malloc(contextStride * contextHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(defaultContextMemory, contextWidth, contextHeight, 8, contextStride, colorSpace, bitmapInfo);
CFRelease(colorSpace);
@ -534,7 +534,7 @@ static void addAttachmentImageCorners(void *memory, const unsigned int width, co
alphaMemory = malloc(contextStride * contextHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(contextMemory, contextWidth, contextHeight, 8, contextStride, colorSpace, bitmapInfo);
CFRelease(colorSpace);
@ -774,7 +774,7 @@ void TGAddImageCorners(void *memory, const unsigned int width, const unsigned in
alphaMemory = malloc(contextStride * contextHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(contextMemory, contextWidth, contextHeight, 8, contextStride, colorSpace, bitmapInfo);
CFRelease(colorSpace);
@ -1053,7 +1053,7 @@ UIImage *TGAverageColorAttachmentWithCornerRadiusImage(UIColor *color, bool atta
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1180,7 +1180,7 @@ TGStaticBackdropAreaData *createImageBackdropArea(uint8_t *sourceImageMemory, in
memset(memory, 0x00, (int)(bytesPerRow * contextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef context = CGBitmapContextCreate(memory, (int)contextSize.width, (int)contextSize.height, 8, bytesPerRow, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
UIGraphicsPushContext(context);
@ -1284,7 +1284,7 @@ UIImage *TGBlurredAttachmentWithCornerRadiusImage(UIImage *source, CGSize size,
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1416,7 +1416,7 @@ UIImage *TGSecretBlurredAttachmentWithCornerRadiusImage(UIImage *source, CGSize
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1546,7 +1546,7 @@ UIImage *TGBlurredFileImage(UIImage *source, CGSize size, uint32_t *averageColor
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1657,7 +1657,7 @@ UIImage *TGBlurredAlphaImage(UIImage *source, CGSize size)
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1733,7 +1733,7 @@ UIImage *TGBlurredRectangularImage(UIImage *source, bool more, CGSize size, CGSi
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef blurredContext = CGBitmapContextCreate(blurredMemory, (int)blurredContextSize.width, (int)blurredContextSize.height, 8, blurredBytesPerRow, colorSpace, bitmapInfo);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1815,7 +1815,7 @@ UIImage *TGLoadedAttachmentWithCornerRadiusImage(UIImage *source, CGSize size, u
void *actionCircleMemory = malloc(((int)(actionCircleBytesPerRow * actionCircleContextSize.height)));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
@ -1907,7 +1907,7 @@ UIImage *TGAnimationFrameAttachmentImage(UIImage *source, CGSize size, CGSize re
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -1954,7 +1954,7 @@ UIImage *TGLoadedFileImage(UIImage *source, CGSize size, uint32_t *averageColor,
memset(actionCircleMemory, 0xff, actionCircleBytesPerRow * actionCircleContextSize.height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
CGContextRef actionCircleContext = CGBitmapContextCreate(actionCircleMemory, (int)actionCircleContextSize.width, (int)actionCircleContextSize.height, 8, actionCircleBytesPerRow, colorSpace, bitmapInfo);
@ -2048,7 +2048,7 @@ UIImage *TGReducedAttachmentWithCornerRadiusImage(UIImage *source, CGSize origin
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
@ -2137,7 +2137,7 @@ UIImage *TGBlurredBackgroundImage(UIImage *source, CGSize size)
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -2173,7 +2173,7 @@ UIImage *TGRoundImage(UIImage *source, CGSize size)
memset(targetMemory, 0, (int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -2213,7 +2213,7 @@ void TGPlainImageAverageColor(UIImage *source, uint32_t *averageColor)
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -2272,7 +2272,7 @@ UIImage *TGCropBackdropImage(UIImage *source, CGSize size)
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -2320,7 +2320,7 @@ UIImage *TGScaleAndCropImageToPixelSize(UIImage *source, CGSize size, CGSize ren
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
@ -2365,7 +2365,7 @@ NSArray *TGBlurredBackgroundImages(UIImage *source, CGSize sourceSize)
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGBitmapInfo bitmapInfo = ((uint32_t)kCGImageAlphaPremultipliedFirst) | ((uint32_t)kCGBitmapByteOrder32Host);
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);

View file

@ -126,9 +126,10 @@
transitionView.alpha = 1.0;
dispatch_async(dispatch_get_main_queue(), ^
{
if (completion != nil)
if (completion != nil) {
completion();
dismissed();
}
dismissed();
});
}];
};

View file

@ -396,24 +396,29 @@
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(CVOpenGLESTextureGetTarget(srcTexture), 0);
if (hasPreviousTexture)
if (hasPreviousTexture) {
glBindTexture(CVOpenGLESTextureGetTarget(prevTexture), 0);
}
glBindTexture(CVOpenGLESTextureGetTarget(dstTexture), 0);
glFlush();
bail:
if (oldContext != _context)
[EAGLContext setCurrentContext:oldContext];
if (oldContext != _context) {
[EAGLContext setCurrentContext:oldContext];
}
if (srcTexture)
CFRelease(srcTexture);
if (srcTexture) {
CFRelease(srcTexture);
}
if (prevTexture)
if (prevTexture) {
CFRelease(prevTexture);
}
if (dstTexture)
CFRelease(dstTexture);
if (dstTexture) {
CFRelease(dstTexture);
}
return dstPixelBuffer;
}

View file

@ -453,7 +453,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGContextRef context = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, ((uint32_t)kCGBitmapByteOrder32Little) | ((uint32_t)kCGImageAlphaPremultipliedFirst));
CGImageRef cgImage = CGBitmapContextCreateImage(context);
CVPixelBufferUnlockBaseAddress(imageBuffer.buffer, 0);
@ -633,7 +633,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
inBuff.data = baseAddress + startpos;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreateWithData(NULL, outWidth, outHeight, 8, outWidth * 4, colorSpace, kCGImageByteOrder32Little | kCGImageAlphaPremultipliedFirst, NULL, nil);
CGContextRef context = CGBitmapContextCreateWithData(NULL, outWidth, outHeight, 8, outWidth * 4, colorSpace, ((uint32_t)kCGImageByteOrder32Little) | ((uint32_t)kCGImageAlphaPremultipliedFirst), NULL, nil);
unsigned char *outImg = CGBitmapContextGetData(context);
vImage_Buffer outBuff = {outImg, outHeight, outWidth, 4 * outWidth};

View file

@ -11,7 +11,7 @@ import AppBundle
import CoreLocation
import PresentationDataUtils
import OpenInExternalAppUI
import ShareController
import DeviceAccess
import UndoUI
import MapKit
@ -207,7 +207,7 @@ public final class LocationViewController: ViewController {
}
if let location = getLocation(from: strongSelf.subject) {
let shareAction = OpenInControllerAction(title: strongSelf.presentationData.strings.Conversation_ContextMenuShare, action: {
strongSelf.present(ShareController(context: context, subject: .mapMedia(location), externalShare: true), in: .window(.root), with: nil)
strongSelf.present(context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .mapMedia(location), externalShare: true)), in: .window(.root), with: nil)
})
strongSelf.present(OpenInActionSheetController(context: context, updatedPresentationData: updatedPresentationData, item: .location(location: location, directions: nil), additionalAction: shareAction, openUrl: params.openUrl), in: .window(.root), with: nil)
}

View file

@ -264,14 +264,11 @@ static NSData *base64_decode(NSString *str) {
- (NSString * _Nonnull)serializeToString {
NSData *data = [self serialize];
if ([data respondsToSelector:@selector(base64EncodedDataWithOptions:)]) {
return [[data base64EncodedStringWithOptions:kNilOptions] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"="]];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [self.serialize base64Encoding];
#pragma clang diagnostic pop
}
NSString *result = [data base64EncodedStringWithOptions:kNilOptions];
result = [result stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
result = [result stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
result = [result stringByReplacingOccurrencesOfString:@"=" withString:@""];
return result;
}
- (BOOL)isEqual:(id)object {

View file

@ -113,6 +113,28 @@ static bool MTFillRandomBytes(uint8_t *buffer, size_t length) {
return SecRandomCopyBytes(kSecRandomDefault, length, buffer) == errSecSuccess;
}
static bool generate_ml_kem_public_key(uint8_t key[1184]) {
for (int i = 0; i < 384; i++) {
uint32_t randA = 0;
uint32_t randB = 0;
if (SecRandomCopyBytes(kSecRandomDefault, 4, (uint8_t *)&randA) != errSecSuccess) {
return false;
}
if (SecRandomCopyBytes(kSecRandomDefault, 4, (uint8_t *)&randB) != errSecSuccess) {
return false;
}
uint32_t a = randA % 3329;
uint32_t b = randB % 3329;
key[i * 3 + 0] = (uint8_t)(a & 0xFF);
key[i * 3 + 1] = (uint8_t)((a >> 8) | ((b & 0x0F) << 4));
key[i * 3 + 2] = (uint8_t)(b >> 4);
}
if (SecRandomCopyBytes(kSecRandomDefault, 32, key + 1152) != errSecSuccess) {
return false;
}
return true;
}
static bool MTGenerateGreaseValues(uint8_t grease[8]) {
if (!MTFillRandomBytes(grease, 8)) {
return false;
@ -137,7 +159,12 @@ typedef enum {
HelloGenerationCommandGrease = 5,
HelloGenerationCommandKey = 6,
HelloGenerationCommandPushLengthPosition = 7,
HelloGenerationCommandPopLengthPosition = 8
HelloGenerationCommandPopLengthPosition = 8,
HelloGenerationCommandMlKemKey = 9,
HelloGenerationCommandBeginChoice = 10,
HelloGenerationCommandBeginAlternative = 11,
HelloGenerationCommandEndAlternative = 12,
HelloGenerationCommandEndChoice = 13
} HelloGenerationCommand;
typedef struct {
@ -148,7 +175,7 @@ static HelloGenerationCommand parseCommand(NSString *string, HelloParseState *st
while (state->position < string.length) {
unichar c = [string characterAtIndex:state->position];
state->position += 1;
if (c == '\n' || c == '\r') {
if (c == '\n' || c == '\r' || c == ' ') {
continue;
} else if (c == 'S') {
return HelloGenerationCommandString;
@ -162,10 +189,20 @@ static HelloGenerationCommand parseCommand(NSString *string, HelloParseState *st
return HelloGenerationCommandGrease;
} else if (c == 'K') {
return HelloGenerationCommandKey;
} else if (c == 'M') {
return HelloGenerationCommandMlKemKey;
} else if (c == '[') {
return HelloGenerationCommandPushLengthPosition;
} else if (c == ']') {
return HelloGenerationCommandPopLengthPosition;
} else if (c == '<') {
return HelloGenerationCommandBeginChoice;
} else if (c == '(') {
return HelloGenerationCommandBeginAlternative;
} else if (c == ')') {
return HelloGenerationCommandEndAlternative;
} else if (c == '>') {
return HelloGenerationCommandEndChoice;
} else {
return HelloGenerationCommandInvalid;
}
@ -187,21 +224,6 @@ static bool parseSpace(NSString *string, HelloParseState *state) {
return seenSpace;
}
static bool parseEndlineOrEnd(NSString *string, HelloParseState *state) {
while (state->position < string.length) {
unichar c = [string characterAtIndex:state->position];
if (c == '\n') {
state->position += 1;
return true;
} else if (c == ' ' || c == '\r') {
state->position += 1;
continue;
} else {
return false;
}
}
return true;
}
static bool parseHexByte(unichar c, uint8_t *output) {
if (c >= '0' && c <= '9') {
@ -284,13 +306,11 @@ static bool parseIntArgument(NSString *string, HelloParseState *state, int *outp
value = value * 10 + (c - '0');
hasDigit = true;
state->position += 1;
} else if (c == ' ') {
state->position += 1;
} else if (c == '\n') {
state->position += 1;
break;
} else {
return false;
break;
}
}
@ -305,14 +325,173 @@ static bool parseIntArgument(NSString *string, HelloParseState *state, int *outp
return true;
}
static bool executeGenerationCodeRecursive(NSString *code, HelloParseState *state, uint8_t grease[8], NSData *domain, id<EncryptionProvider> provider, NSMutableData *resultData, NSMutableArray<NSNumber *> *lengthStack) {
while (true) {
HelloGenerationCommand command = parseCommand(code, state);
if (command == HelloGenerationCommandInvalid) {
break;
}
switch (command) {
case HelloGenerationCommandString: {
if (!parseSpace(code, state)) {
return false;
}
NSData *data = parseHexStringArgument(code, state);
if (data == nil) {
return false;
}
[resultData appendData:data];
break;
}
case HelloGenerationCommandZero: {
if (!parseSpace(code, state)) {
return false;
}
int zeroLength = 0;
if (!parseIntArgument(code, state, &zeroLength)) {
return false;
}
if (zeroLength < 0) {
return false;
}
NSMutableData *zeros = [[NSMutableData alloc] initWithLength:(NSUInteger)zeroLength];
[resultData appendData:zeros];
break;
}
case HelloGenerationCommandRandom: {
if (!parseSpace(code, state)) {
return false;
}
int randomLength = 0;
if (!parseIntArgument(code, state, &randomLength)) {
return false;
}
if (randomLength < 0) {
return false;
}
NSMutableData *randomData = [[NSMutableData alloc] initWithLength:(NSUInteger)randomLength];
if (!MTFillRandomBytes((uint8_t *)randomData.mutableBytes, randomData.length)) {
return false;
}
[resultData appendData:randomData];
break;
}
case HelloGenerationCommandDomain: {
[resultData appendData:domain];
break;
}
case HelloGenerationCommandGrease: {
if (!parseSpace(code, state)) {
return false;
}
int greaseIndex = 0;
if (!parseIntArgument(code, state, &greaseIndex)) {
return false;
}
if (greaseIndex < 0 || greaseIndex >= 8) {
return false;
}
uint8_t value = grease[greaseIndex];
[resultData appendBytes:&value length:1];
[resultData appendBytes:&value length:1];
break;
}
case HelloGenerationCommandKey: {
NSMutableData *key = [[NSMutableData alloc] initWithLength:32];
generate_public_key((unsigned char *)key.mutableBytes, provider);
[resultData appendData:key];
break;
}
case HelloGenerationCommandMlKemKey: {
NSMutableData *key = [[NSMutableData alloc] initWithLength:1184];
if (!generate_ml_kem_public_key((uint8_t *)key.mutableBytes)) {
return false;
}
[resultData appendData:key];
break;
}
case HelloGenerationCommandPushLengthPosition: {
NSUInteger lengthPosition = resultData.length;
uint8_t zeroBytes[2] = { 0, 0 };
[resultData appendBytes:zeroBytes length:2];
[lengthStack addObject:@(lengthPosition)];
break;
}
case HelloGenerationCommandPopLengthPosition: {
if (lengthStack.count == 0) {
return false;
}
NSUInteger position = (NSUInteger)[lengthStack.lastObject unsignedIntegerValue];
[lengthStack removeLastObject];
if (resultData.length < position + 2) {
return false;
}
uint16_t blockLength = (uint16_t)(resultData.length - position - 2);
((uint8_t *)resultData.mutableBytes)[position] = (uint8_t)((blockLength >> 8) & 0xff);
((uint8_t *)resultData.mutableBytes)[position + 1] = (uint8_t)(blockLength & 0xff);
break;
}
case HelloGenerationCommandBeginChoice: {
NSMutableArray<NSMutableData *> *alternatives = [[NSMutableArray alloc] init];
while (true) {
HelloGenerationCommand nextCommand = parseCommand(code, state);
if (nextCommand == HelloGenerationCommandEndChoice) {
break;
}
if (nextCommand != HelloGenerationCommandBeginAlternative) {
return false;
}
NSMutableData *altData = [[NSMutableData alloc] init];
NSMutableArray<NSNumber *> *altLengthStack = [[NSMutableArray alloc] init];
if (!executeGenerationCodeRecursive(code, state, grease, domain, provider, altData, altLengthStack)) {
return false;
}
if (altLengthStack.count != 0) {
return false;
}
[alternatives addObject:altData];
}
if (alternatives.count == 0) {
return false;
}
uint32_t choiceIndex = arc4random_uniform((uint32_t)alternatives.count);
[resultData appendData:alternatives[choiceIndex]];
break;
}
case HelloGenerationCommandEndAlternative: {
// signals end of current alternative return to caller
return true;
}
case HelloGenerationCommandBeginAlternative:
case HelloGenerationCommandEndChoice: {
// should not be encountered directly at this level
return false;
}
default: {
return false;
}
}
}
return true;
}
static NSMutableData *executeGenerationCode(id<EncryptionProvider> provider, NSData *domain) {
NSString *code = @"S \"\\x16\\x03\\x01\\x02\\x00\\x01\\x00\\x01\\xfc\\x03\\x03\"\n"
NSString *code = @"S \"\\x16\\x03\\x01\"\n"
"[\n"
"S \"\\x01\\x00\"\n"
"[\n"
"S \"\\x03\\x03\"\n"
"Z 32\n"
"S \"\\x20\"\n"
"R 32\n"
"S \"\\x00\\x2a\"\n"
"G 0\n"
"S \"\\x13\\x01\\x13\\x02\\x13\\x03\\xc0\\x2c\\xc0\\x2b\\xcc\\xa9\\xc0\\x30\\xc0\\x2f\\xcc\\xa8\\xc0\\x0a\\xc0\\x09\\xc0\\x14\\xc0\\x13\\x00\\x9d\\x00\\x9c\\x00\\x35\\x00\\x2f\\xc0\\x08\\xc0\\x12\\x00\\x0a\\x01\\x00\\x01\\x89\"\n"
"<\n"
"( S \"\\x00\\x1c\" G 0 S \"\\x13\\x02\\x13\\x01\\x13\\x03\\xc0\\x2c\\xc0\\x30\\xc0\\x2b\\xcc\\xa9\\xc0\\x2f\\xcc\\xa8\\xc0\\x0a\\xc0\\x09\\xc0\\x14\\xc0\\x13\" )\n"
"( S \"\\x00\\x2a\" G 0 S \"\\x13\\x02\\x13\\x03\\x13\\x01\\xc0\\x2c\\xc0\\x2b\\xcc\\xa9\\xc0\\x30\\xc0\\x2f\\xcc\\xa8\\xc0\\x0a\\xc0\\x09\\xc0\\x14\\xc0\\x13\\x00\\x9d\\x00\\x9c\\x00\\x35\\x00\\x2f\\xc0\\x08\\xc0\\x12\\x00\\x0a\" )\n"
">\n"
"S \"\\x01\\x00\"\n"
"[\n"
"G 2\n"
"S \"\\x00\\x00\\x00\\x00\"\n"
"[\n"
@ -323,17 +502,28 @@ static NSMutableData *executeGenerationCode(id<EncryptionProvider> provider, NSD
"]\n"
"]\n"
"]\n"
"S \"\\x00\\x17\\x00\\x00\\xff\\x01\\x00\\x01\\x00\\x00\\x0a\\x00\\x0c\\x00\\x0a\"\n"
"S \"\\x00\\x17\\x00\\x00\\xff\\x01\\x00\\x01\\x00\\x00\\x0a\\x00\\x0e\\x00\\x0c\"\n"
"G 4\n"
"S \"\\x00\\x1d\\x00\\x17\\x00\\x18\\x00\\x19\\x00\\x0b\\x00\\x02\\x01\\x00\\x00\\x10\\x00\\x0e\\x00\\x0c\\x02\\x68\\x32\\x08\\x68\\x74\\x74\\x70\\x2f\\x31\\x2e\\x31\\x00\\x05\\x00\\x05\\x01\\x00\\x00\\x00\\x00\\x00\\x0d\\x00\\x16\\x00\\x14\\x04\\x03\\x08\\x04\\x04\\x01\\x05\\x03\\x08\\x05\\x08\\x05\\x05\\x01\\x08\\x06\\x06\\x01\\x02\\x01\\x00\\x12\\x00\\x00\\x00\\x33\\x00\\x2b\\x00\\x29\"\n"
"S \"\\x11\\xec\\x00\\x1d\\x00\\x17\\x00\\x18\\x00\\x19\\x00\\x0b\\x00\\x02\\x01\\x00\"\n"
"<\n"
"( S \"\\x00\\x10\\x00\\x0b\\x00\\x09\\x08\\x68\\x74\\x74\\x70\\x2f\\x31\\x2e\\x31\" )\n"
"( S \"\\x00\\x10\\x00\\x0e\\x00\\x0c\\x02\\x68\\x32\\x08\\x68\\x74\\x74\\x70\\x2f\\x31\\x2e\\x31\" )\n"
">\n"
"S \"\\x00\\x05\\x00\\x05\\x01\\x00\\x00\\x00\\x00\\x00\\x0d\\x00\\x16\\x00\\x14\\x04\\x03\\x08\\x04\\x04\\x01\\x05\\x03\\x08\\x05\\x08\\x05\\x05\\x01\\x08\\x06\\x06\\x01\\x02\\x01\\x00\\x12\\x00\\x00\\x00\\x33\\x04\\xef\\x04\\xed\"\n"
"G 4\n"
"S \"\\x00\\x01\\x00\\x00\\x1d\\x00\\x20\"\n"
"S \"\\x00\\x01\\x00\\x11\\xec\\x04\\xc0\"\n"
"M\n"
"K\n"
"S \"\\x00\\x2d\\x00\\x02\\x01\\x01\\x00\\x2b\\x00\\x0b\\x0a\"\n"
"S \"\\x00\\x1d\\x00\\x20\"\n"
"K\n"
"S \"\\x00\\x2d\\x00\\x02\\x01\\x01\\x00\\x2b\\x00\\x07\\x06\"\n"
"G 6\n"
"S \"\\x03\\x04\\x03\\x03\\x03\\x02\\x03\\x01\\x00\\x1b\\x00\\x03\\x02\\x00\\x01\"\n"
"S \"\\x03\\x04\\x03\\x03\\x00\\x1b\\x00\\x03\\x02\\x00\\x01\"\n"
"G 3\n"
"S \"\\x00\\x01\\x00\"\n";
"S \"\\x00\\x01\\x00\"\n"
"]\n"
"]\n"
"]\n";
uint8_t grease[8];
if (!MTGenerateGreaseValues(grease)) {
@ -345,120 +535,8 @@ static NSMutableData *executeGenerationCode(id<EncryptionProvider> provider, NSD
HelloParseState state = { .position = 0 };
while (true) {
HelloGenerationCommand command = parseCommand(code, &state);
if (command == HelloGenerationCommandInvalid) {
break;
}
switch (command) {
case HelloGenerationCommandString: {
if (!parseSpace(code, &state)) {
return nil;
}
NSData *data = parseHexStringArgument(code, &state);
if (data == nil) {
return nil;
}
[resultData appendData:data];
break;
}
case HelloGenerationCommandZero: {
if (!parseSpace(code, &state)) {
return nil;
}
int zeroLength = 0;
if (!parseIntArgument(code, &state, &zeroLength)) {
return nil;
}
if (zeroLength < 0) {
return nil;
}
NSMutableData *zeros = [[NSMutableData alloc] initWithLength:(NSUInteger)zeroLength];
[resultData appendData:zeros];
break;
}
case HelloGenerationCommandRandom: {
if (!parseSpace(code, &state)) {
return nil;
}
int randomLength = 0;
if (!parseIntArgument(code, &state, &randomLength)) {
return nil;
}
if (randomLength < 0) {
return nil;
}
NSMutableData *randomData = [[NSMutableData alloc] initWithLength:(NSUInteger)randomLength];
if (!MTFillRandomBytes((uint8_t *)randomData.mutableBytes, randomData.length)) {
return nil;
}
[resultData appendData:randomData];
break;
}
case HelloGenerationCommandDomain: {
[resultData appendData:domain];
if (!parseEndlineOrEnd(code, &state)) {
return nil;
}
break;
}
case HelloGenerationCommandGrease: {
if (!parseSpace(code, &state)) {
return nil;
}
int greaseIndex = 0;
if (!parseIntArgument(code, &state, &greaseIndex)) {
return nil;
}
if (greaseIndex < 0 || greaseIndex >= 8) {
return nil;
}
uint8_t value = grease[greaseIndex];
[resultData appendBytes:&value length:1];
[resultData appendBytes:&value length:1];
break;
}
case HelloGenerationCommandKey: {
if (!parseEndlineOrEnd(code, &state)) {
return nil;
}
NSMutableData *key = [[NSMutableData alloc] initWithLength:32];
generate_public_key((unsigned char *)key.mutableBytes, provider);
[resultData appendData:key];
break;
}
case HelloGenerationCommandPushLengthPosition: {
if (!parseEndlineOrEnd(code, &state)) {
return nil;
}
NSUInteger lengthPosition = resultData.length;
uint8_t zeroBytes[2] = { 0, 0 };
[resultData appendBytes:zeroBytes length:2];
[lengthStack addObject:@(lengthPosition)];
break;
}
case HelloGenerationCommandPopLengthPosition: {
if (!parseEndlineOrEnd(code, &state)) {
return nil;
}
if (lengthStack.count == 0) {
return nil;
}
NSUInteger position = (NSUInteger)[lengthStack.lastObject unsignedIntegerValue];
[lengthStack removeLastObject];
if (resultData.length < position + 2) {
return nil;
}
uint16_t blockLength = (uint16_t)(resultData.length - position - 2);
((uint8_t *)resultData.mutableBytes)[position] = (uint8_t)((blockLength >> 8) & 0xff);
((uint8_t *)resultData.mutableBytes)[position + 1] = (uint8_t)(blockLength & 0xff);
break;
}
default: {
return nil;
}
}
if (!executeGenerationCodeRecursive(code, &state, grease, domain, provider, resultData, lengthStack)) {
return nil;
}
if (lengthStack.count != 0) {

View file

@ -39,7 +39,7 @@ public class SetupTwoStepVerificationController: ViewController {
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: self.presentationData.theme.rootController.navigationBar.accentTextColor, disabledButtonColor: self.presentationData.theme.rootController.navigationBar.disabledButtonColor, primaryTextColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentForegroundColor: self.presentationData.theme.list.itemCheckColors.foregroundColor), strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style

View file

@ -71,7 +71,7 @@ public final class TwoFactorDataInputScreen: ViewController {
self.presentationData = self.sharedContext.currentPresentationData.with { $0 }
let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))

View file

@ -57,7 +57,7 @@ public final class TwoFactorAuthSplashScreen: ViewController {
self.presentationData = self.sharedContext.currentPresentationData.with { $0 }
let defaultTheme = NavigationBarTheme(rootControllerTheme: self.presentationData.theme)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: defaultTheme.overallDarkAppearance, buttonColor: defaultTheme.buttonColor, disabledButtonColor: defaultTheme.disabledButtonColor, primaryTextColor: defaultTheme.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: defaultTheme.badgeBackgroundColor, badgeStrokeColor: defaultTheme.badgeStrokeColor, badgeTextColor: defaultTheme.badgeTextColor, accentButtonColor: defaultTheme.accentButtonColor, accentForegroundColor: defaultTheme.accentForegroundColor)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))

View file

@ -7,7 +7,6 @@ import TelegramCore
import TelegramPresentationData
import AccountContext
import RadialStatusNode
import ShareController
import PhotoResources
import GalleryUI
import TelegramUniversalVideoContent
@ -201,12 +200,11 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
forceTheme = defaultDarkColorPresentationTheme
}
let shareController = ShareController(context: strongSelf.context, subject: subject, preferredAction: .saveToCameraRoll, forceTheme: forceTheme)
shareController.actionCompleted = {
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: subject, preferredAction: .saveToCameraRoll, forceTheme: forceTheme, actionCompleted: {
if let actionCompletionText = actionCompletionText {
interaction.presentController(UndoOverlayController(presentationData: presentationData, content: .mediaSaved(text: actionCompletionText), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return true }), nil)
}
}
}))
interaction.presentController(shareController, nil)
}
}

View file

@ -1562,11 +1562,10 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta
guard let inviteLink = invite.link else {
return
}
let shareController = ShareController(context: context, subject: .url(inviteLink), updatedPresentationData: updatedPresentationData)
shareController.actionCompleted = {
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(inviteLink), updatedPresentationData: updatedPresentationData, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.InviteLink_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
}, linkContextAction: { node, gesture in
guard let node = node as? ContextReferenceContentNode, let controller = getControllerImpl?() else {

View file

@ -10,6 +10,7 @@ import ItemListUI
import PresentationDataUtils
import TelegramStringFormatting
import AccountContext
import ShareController
import AlertUI
import PresentationDataUtils
import PhotoResources
@ -24,7 +25,6 @@ import UndoUI
import GalleryUI
import PeerAvatarGalleryUI
import Postbox
import ShareController
import ContextUI
private enum DeviceContactInfoAction {
@ -1516,8 +1516,22 @@ public func pushContactContextOptionsController(context: AccountContext, context
.action(ContextMenuActionItem(text: presentationData.strings.Chat_Context_Phone_CreateNewContact, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AddUser"), color: theme.contextMenu.primaryColor) }, action: { _, f in
f(.default)
push(context.sharedContext.makeDeviceContactInfoController(context: ShareControllerAppAccountContext(context: context), environment: ShareControllerAppEnvironment(sharedContext: context.sharedContext), subject: .create(peer: peer?._asPeer(), contactData: contactData, isSharing: peer != nil, shareViaException: false, completion: { _, _, _ in
}), completed: nil, cancelled: nil))
context.sharedContext.openAddContact(
context: context,
peer: peer,
firstName: contactData.basicData.firstName,
lastName: contactData.basicData.lastName,
phoneNumber: contactData.basicData.phoneNumbers.first?.value ?? "",
label: contactData.basicData.phoneNumbers.first?.label ?? "mobile",
present: { [weak parentController] c, a in
parentController?.present(c, in: .window(.root), with: a)
},
pushController: { c in
push(c)
},
completed: {
}
)
}))
)
items.append(

View file

@ -13,7 +13,6 @@ import TextFormat
import OverlayStatusController
import TelegramStringFormatting
import AccountContext
import ShareController
import AlertUI
import PresentationDataUtils
import TelegramNotices
@ -62,6 +61,8 @@ public func openAddPersonContactImpl(context: AccountContext, updatedPresentatio
let controller = context.sharedContext.makeNewContactScreen(
context: context,
peer: peer,
firstName: nil,
lastName: nil,
phoneNumber: user.phone,
shareViaException: shareViaException,
completion: { peer, _, _ in

View file

@ -1213,9 +1213,9 @@ public final class Transaction {
self.postbox?.reindexUnreadCounters(currentTransaction: self)
}
public func searchPeers(query: String) -> [RenderedPeer] {
public func searchPeers(query: String, predicate: ChatListFilterPredicate?) -> [RenderedPeer] {
assert(!self.disposed)
return self.postbox?.searchPeers(query: query) ?? []
return self.postbox?.searchPeers(transaction: self, query: query, predicate: predicate) ?? []
}
public func clearTimestampBasedAttribute(id: MessageId, tag: UInt16) {
@ -3800,13 +3800,13 @@ final class PostboxImpl {
} |> switchToLatest
}
public func searchPeers(query: String) -> Signal<[RenderedPeer], NoError> {
public func searchPeers(query: String, predicate: ChatListFilterPredicate?) -> Signal<[RenderedPeer], NoError> {
return self.transaction { transaction -> Signal<[RenderedPeer], NoError> in
return .single(transaction.searchPeers(query: query))
return .single(transaction.searchPeers(query: query, predicate: predicate))
} |> switchToLatest
}
fileprivate func searchPeers(query: String) -> [RenderedPeer] {
fileprivate func searchPeers(transaction: Transaction, query: String, predicate: ChatListFilterPredicate?) -> [RenderedPeer] {
var peerIds = Set<PeerId>()
var chatPeers: [RenderedPeer] = []
@ -3823,6 +3823,30 @@ final class PostboxImpl {
}
chatPeerIds.append(contentsOf: additionalChatPeerIds)
if let predicate {
let globalNotificationSettings = self.getGlobalNotificationSettings(transaction: transaction)
let filterImpl: (PeerId) -> Bool = { peerId in
guard let peer = self.peerTable.get(peerId) else {
return false
}
let inclusion = self.chatListIndexTable.get(peerId: peerId)
let isUnread = self.readStateTable.getCombinedState(peerId)?.isUnread ?? false
let notificationsPeerId = peer.notificationSettingsPeerId ?? peerId
let isContact = self.contactsTable.isContact(peerId: notificationsPeerId)
let isRemovedFromTotalUnreadCount = resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettings, peer: peer, peerSettings: self.peerNotificationSettingsTable.getEffective(notificationsPeerId))
let messageTagSummaryResult = resolveChatListMessageTagSummaryResultCalculation(postbox: self, peerId: peer.id, threadId: nil, calculation: predicate.messageTagSummary)
if predicate.includes(peer: peer, groupId: inclusion.inclusion.groupId ?? .root, isRemovedFromTotalUnreadCount: isRemovedFromTotalUnreadCount, isUnread: isUnread, isContact: isContact, messageTagSummaryResult: messageTagSummaryResult) {
return true
} else {
return false
}
}
chatPeerIds = chatPeerIds.filter(filterImpl)
contactPeerIds = contactPeerIds.filter(filterImpl)
}
for peerId in chatPeerIds {
if let peer = self.peerTable.get(peerId) {
var peers = SimpleDictionary<PeerId, Peer>()
@ -4929,12 +4953,12 @@ public class Postbox {
}
}
public func searchPeers(query: String) -> Signal<[RenderedPeer], NoError> {
public func searchPeers(query: String, predicate: ChatListFilterPredicate? = nil) -> Signal<[RenderedPeer], NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.searchPeers(query: query).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
disposable.set(impl.searchPeers(query: query, predicate: predicate).start(next: subscriber.putNext, error: subscriber.putError, completed: subscriber.putCompletion))
}
return disposable

View file

@ -77,7 +77,7 @@ public final class QrCodeScanScreen: ViewController {
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear)
let navigationBarTheme = NavigationBarTheme(overallDarkAppearance: self.presentationData.theme.overallDarkAppearance, buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear, accentButtonColor: .white, accentForegroundColor: .black)
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))

View file

@ -30,6 +30,7 @@ swift_library(
"//submodules/TelegramUI/Components/EntityKeyboard:EntityKeyboard",
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
"//submodules/TelegramUI/Components/DCTMultiAnimationRendererImpl:DCTMultiAnimationRendererImpl",
"//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView",
"//submodules/Components/ComponentDisplayAdapters:ComponentDisplayAdapters",
"//submodules/TextFormat:TextFormat",

View file

@ -20,6 +20,7 @@ import EntityKeyboard
import ComponentDisplayAdapters
import AnimationCache
import MultiAnimationRenderer
import DCTMultiAnimationRendererImpl
import EmojiTextAttachmentView
import TextFormat
import GZip
@ -504,8 +505,8 @@ public final class ReactionContextNode: ASDisplayNode, ASScrollViewDelegate {
self.reactionsLocked = reactionsLocked
self.animationCache = animationCache
self.animationRenderer = MultiAnimationRendererImpl()
(self.animationRenderer as? MultiAnimationRendererImpl)?.useYuvA = context.sharedContext.immediateExperimentalUISettings.compressedEmojiCache
self.animationRenderer = DCTMultiAnimationRendererImpl()
(self.animationRenderer as? DCTMultiAnimationRendererImpl)?.useYuvA = context.sharedContext.immediateExperimentalUISettings.compressedEmojiCache
self.backgroundMaskNode = ASDisplayNode()
var backgroundGlassParams: ReactionContextBackgroundNode.GlassParams?
@ -3341,7 +3342,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
if let currentItemNode = currentItemNode {
itemNode = currentItemNode
} else {
let animationRenderer = MultiAnimationRendererImpl()
let animationRenderer = DCTMultiAnimationRendererImpl()
itemNode = ReactionNode(context: context, theme: theme, item: reaction, icon: .none, animationCache: animationCache, animationRenderer: animationRenderer, loopIdle: false, isLocked: false)
}
self.itemNode = itemNode

View file

@ -89,50 +89,111 @@ public func fetchMediaData(context: AccountContext, postbox: Postbox, userLocati
}
}
public func saveToCameraRoll(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, mediaReference: AnyMediaReference) -> Signal<Float, NoError> {
return fetchMediaData(context: context, postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, mediaReference: mediaReference)
|> mapToSignal { state, isImage -> Signal<Float, NoError> in
switch state {
public func saveToCameraRoll(context: AccountContext, postbox: Postbox, userLocation: MediaResourceUserLocation, customUserContentType: MediaResourceUserContentType? = nil, mediaReference: AnyMediaReference, video: AnyMediaReference? = nil) -> Signal<Float, NoError> {
let mediaData: Signal<(FetchMediaDataState, Bool), NoError> = fetchMediaData(context: context, postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, mediaReference: mediaReference)
let videoData: Signal<FetchMediaDataState?, NoError>
if let video {
videoData = fetchMediaData(context: context, postbox: postbox, userLocation: userLocation, customUserContentType: customUserContentType, mediaReference: video)
|> map { state, _ in
return state
}
|> map(Optional.init)
} else {
videoData = .single(nil)
}
return combineLatest(
queue: Queue.mainQueue(),
mediaData,
videoData
)
|> mapToSignal { stateAndIsImage, videoStateAndIsImage -> Signal<Float, NoError> in
let isImage = stateAndIsImage.1
var mainData: MediaResourceData?
var videoData: MediaResourceData?
var waitForVideo = false
if let videoState = videoStateAndIsImage {
switch videoState {
case let .progress(value):
return .single(value * 0.95)
case let .data(data):
videoData = data
}
switch stateAndIsImage.0 {
case let .progress(value):
return .single(0.95 + 0.05 * value)
case let .data(data):
mainData = data
}
waitForVideo = true
} else {
switch stateAndIsImage.0 {
case let .progress(value):
return .single(value)
case let .data(data):
if data.complete {
return Signal<Float, NoError> { subscriber in
DeviceAccess.authorizeAccess(to: .mediaLibrary(.save), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in
context.sharedContext.presentGlobalController(c, a)
}, openSettings: context.sharedContext.applicationBindings.openSettings, { authorized in
if !authorized {
subscriber.putCompletion()
return
}
let tempVideoPath = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max)).mp4"
mainData = data
}
}
if let mainData, mainData.complete, videoData != nil || !waitForVideo {
return Signal<Float, NoError> { subscriber in
DeviceAccess.authorizeAccess(to: .mediaLibrary(.save), presentationData: context.sharedContext.currentPresentationData.with { $0 }, present: { c, a in
context.sharedContext.presentGlobalController(c, a)
}, openSettings: context.sharedContext.applicationBindings.openSettings, { authorized in
if !authorized {
subscriber.putCompletion()
return
}
let tempVideoPath = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max)).mp4"
if isImage, let videoData, let imageData = try? Data(contentsOf: URL(fileURLWithPath: mainData.path)) {
let id = UUID().uuidString
let jpegWithID = addAssetIdentifierToJPEG(imageData, assetIdentifier: id)!
let outputVideoURL = URL(fileURLWithPath: NSTemporaryDirectory() + "\(id).mov")
try? FileManager.default.copyItem(atPath: videoData.path, toPath: tempVideoPath)
addAssetIdentifierToVideo(inputURL: URL(fileURLWithPath: tempVideoPath), outputURL: outputVideoURL, assetIdentifier: id) { success in
guard success else { return }
PHPhotoLibrary.shared().performChanges({
if isImage {
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
PHAssetCreationRequest.forAsset().addResource(with: .photo, data: fileData, options: nil)
}
} else {
if let _ = try? FileManager.default.copyItem(atPath: data.path, toPath: tempVideoPath) {
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: tempVideoPath))
}
}
let request = PHAssetCreationRequest.forAsset()
request.addResource(with: .photo, data: jpegWithID, options: nil)
request.addResource(with: .pairedVideo, fileURL: outputVideoURL, options: nil)
}, completionHandler: { _, error in
if let error = error {
print("\(error)")
}
let _ = try? FileManager.default.removeItem(atPath: tempVideoPath)
subscriber.putNext(1.0)
subscriber.putCompletion()
})
})
return ActionDisposable {
}
} else {
PHPhotoLibrary.shared().performChanges({
if isImage {
if let imageData = try? Data(contentsOf: URL(fileURLWithPath: mainData.path)) {
PHAssetCreationRequest.forAsset().addResource(with: .photo, data: imageData, options: nil)
}
} else {
if let _ = try? FileManager.default.copyItem(atPath: mainData.path, toPath: tempVideoPath) {
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: tempVideoPath))
}
}
}, completionHandler: { _, error in
if let error {
print("\(error)")
}
let _ = try? FileManager.default.removeItem(atPath: tempVideoPath)
subscriber.putNext(1.0)
subscriber.putCompletion()
})
}
} else {
return .complete()
})
return ActionDisposable {
}
}
} else {
return .complete()
}
}
}
@ -160,3 +221,56 @@ public func copyToPasteboard(context: AccountContext, postbox: Postbox, userLoca
}
|> mapToSignal { _ -> Signal<Void, NoError> in return .complete() }
}
private func addAssetIdentifierToJPEG(_ imageData: Data, assetIdentifier: String) -> Data? {
guard let source = CGImageSourceCreateWithData(imageData as CFData, nil), let uti = CGImageSourceGetType(source), let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil) else {
return nil
}
let mutableData = NSMutableData()
guard let destination = CGImageDestinationCreateWithData(mutableData, uti, 1, nil) else {
return nil
}
var metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [String: Any] ?? [:]
var maker = metadata[kCGImagePropertyMakerAppleDictionary as String] as? [String: Any] ?? [:]
maker["17"] = assetIdentifier
metadata[kCGImagePropertyMakerAppleDictionary as String] = maker
CGImageDestinationAddImage(destination, cgImage, metadata as CFDictionary)
CGImageDestinationFinalize(destination)
return mutableData as Data
}
private func addAssetIdentifierToVideo(inputURL: URL, outputURL: URL, assetIdentifier: String, completion: @escaping (Bool) -> Void) {
let asset = AVAsset(url: inputURL)
guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetPassthrough) else {
completion(false)
return
}
let identifierItem = AVMutableMetadataItem()
identifierItem.keySpace = .quickTimeMetadata
identifierItem.key = AVMetadataKey.quickTimeMetadataKeyContentIdentifier as NSString
identifierItem.value = assetIdentifier as NSString
let stillImageTimeItem = AVMutableMetadataItem()
let keyStillImageTime = "com.apple.quicktime.still-image-time"
let keySpaceQuickTimeMetadata = "mdta"
stillImageTimeItem.key = keyStillImageTime as (NSCopying & NSObjectProtocol)?
stillImageTimeItem.keySpace = AVMetadataKeySpace(rawValue: keySpaceQuickTimeMetadata)
stillImageTimeItem.value = 0 as (NSCopying & NSObjectProtocol)?
stillImageTimeItem.dataType = "com.apple.metadata.datatype.int8"
exportSession.outputURL = outputURL
exportSession.outputFileType = .mov
exportSession.metadata = [identifierItem, stillImageTimeItem]
exportSession.shouldOptimizeForNetworkUse = true
exportSession.exportAsynchronously {
completion(exportSession.status == .completed)
}
}

View file

@ -928,6 +928,9 @@ public class SearchBarNode: ASDisplayNode, UITextFieldDelegate {
} set {
self.textField.tokens = newValue
self.updateIsEmpty(animated: true)
if let (boundingSize, leftInset, rightInset) = self.validLayout {
self.updateLayout(boundingSize: boundingSize, leftInset: leftInset, rightInset: rightInset, transition: .immediate)
}
}
}

View file

@ -54,6 +54,7 @@ public final class SearchBarPlaceholderContentView: UIView {
let fieldStyle: SearchBarStyle
let plainBackgroundView: UIImageView
let glassBackgroundContainerView: GlassBackgroundContainerView?
let glassBackgroundView: GlassBackgroundView?
private var fillBackgroundColor: UIColor
private var foregroundColor: UIColor
@ -82,8 +83,10 @@ public final class SearchBarPlaceholderContentView: UIView {
switch fieldStyle {
case .legacy, .modern:
self.glassBackgroundContainerView = nil
self.glassBackgroundView = nil
case .inlineNavigation, .glass:
self.glassBackgroundContainerView = GlassBackgroundContainerView()
self.glassBackgroundView = GlassBackgroundView()
}
@ -111,8 +114,9 @@ public final class SearchBarPlaceholderContentView: UIView {
self.plainBackgroundView.addSubview(self.plainIconNode.view)
self.plainBackgroundView.addSubview(self.plainLabelNode.view)
if let glassBackgroundView = self.glassBackgroundView {
self.addSubview(glassBackgroundView)
if let glassBackgroundContainerView = self.glassBackgroundContainerView, let glassBackgroundView = self.glassBackgroundView {
self.addSubview(glassBackgroundContainerView)
glassBackgroundContainerView.contentView.addSubview(glassBackgroundView)
glassBackgroundView.contentView.addSubview(self.iconNode.view)
glassBackgroundView.contentView.addSubview(self.labelNode.view)
@ -300,9 +304,14 @@ public final class SearchBarPlaceholderContentView: UIView {
transition.updateFrame(view: self.plainBackgroundView, frame: CGRect(origin: CGPoint(), size: CGSize(width: params.constrainedSize.width, height: height)))
}
if let glassBackgroundView = self.glassBackgroundView {
transition.updatePosition(layer: glassBackgroundView.layer, position: backgroundFrame.center)
if let glassBackgroundContainerView = self.glassBackgroundContainerView, let glassBackgroundView = self.glassBackgroundView {
transition.updatePosition(layer: glassBackgroundContainerView.layer, position: backgroundFrame.center)
transition.updateBounds(layer: glassBackgroundContainerView.layer, bounds: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updatePosition(layer: glassBackgroundView.layer, position: CGRect(origin: CGPoint(), size: backgroundFrame.size).center)
transition.updateBounds(layer: glassBackgroundView.layer, bounds: CGRect(origin: CGPoint(), size: backgroundFrame.size))
var backgroundAlpha: CGFloat = 1.0
if backgroundFrame.height < 16.0 {
backgroundAlpha = max(0.0, min(1.0, backgroundFrame.height / 16.0))
@ -310,9 +319,10 @@ public final class SearchBarPlaceholderContentView: UIView {
if !params.isActive {
backgroundAlpha = 0.0
}
ComponentTransition(transition).setAlpha(view: glassBackgroundView, alpha: backgroundAlpha)
ComponentTransition(transition).setAlpha(view: glassBackgroundContainerView, alpha: backgroundAlpha)
let isDark = params.backgroundColor.hsb.b < 0.5
if params.isActive {
glassBackgroundContainerView.update(size: backgroundFrame.size, isDark: isDark, transition: ComponentTransition(transition))
glassBackgroundView.update(size: backgroundFrame.size, cornerRadius: backgroundFrame.height * 0.5, isDark: isDark, tintColor: .init(kind: params.preferClearGlass ? .clear : .panel), isInteractive: true, transition: ComponentTransition(transition))
}

View file

@ -9,8 +9,8 @@ import MtProtoKit
import ItemListUI
import PresentationDataUtils
import AccountContext
import UrlEscaping
import ShareController
import UrlEscaping
private final class ProxySettingsControllerArguments {
let toggleEnabled: (Bool) -> Void

View file

@ -10,14 +10,13 @@ import PresentationDataUtils
import AccountContext
import UrlEscaping
import UrlHandling
import ShareController
private func shareLink(for server: ProxyServerSettings) -> String {
var link: String
switch server.connection {
case let .mtp(secret):
let secret = MTProxySecret.parseData(secret)?.serializeToString() ?? ""
link = "https://t.me/proxy?server=\(server.host)&port=\(server.port)"
link = "tg://proxy?server=\(server.host)&port=\(server.port)"
link += "&secret=\(secret.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryValueAllowed) ?? "")"
case let .socks5(username, password):
link = "https://t.me/socks?server=\(server.host)&port=\(server.port)"
@ -333,10 +332,10 @@ func proxyServerSettingsController(sharedContext: SharedAccountContext, context:
let signal = combineLatest(updatedPresentationData, statePromise.get())
|> deliverOnMainQueue
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
let leftNavigationButton = ItemListNavigationButton(content: .text("___close"), style: .regular, enabled: true, action: {
dismissImpl?()
})
let rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: state.isComplete, action: {
let rightNavigationButton = ItemListNavigationButton(content: .text("___done"), style: .bold, enabled: state.isComplete, action: {
if let proxyServerSettings = proxyServerSettings(with: state) {
let _ = (updateProxySettingsInteractively(accountManager: accountManager, { settings in
var settings = settings

View file

@ -7,7 +7,6 @@ import UIKit
import SwiftSignalKit
import TelegramPresentationData
import QrCode
import ShareController
public final class ShareProxyServerActionSheetController: ActionSheetController {
private var presentationDisposable: Disposable?

View file

@ -10,7 +10,7 @@ import MergeLists
import ItemListUI
import PresentationDataUtils
import AccountContext
import ShareController
import SearchBarNode
import SearchUI
import UndoUI
@ -793,13 +793,12 @@ final class LocalizationListControllerNode: ViewControllerTracingNode {
guard let strongSelf = self else {
return
}
let shareController = ShareController(context: strongSelf.context, subject: .url("https://t.me/setlanguage/\(info.languageCode)"))
shareController.actionCompleted = { [weak self] in
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .url("https://t.me/setlanguage/\(info.languageCode)"), actionCompleted: { [weak self] in
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}
}))
strongSelf.present(shareController, nil)
}))
controller.setItemGroups([

View file

@ -13,7 +13,6 @@ import AccountContext
import StickerPackPreviewUI
import ItemListStickerPackItem
import UndoUI
import ShareController
public enum ArchivedStickerPacksControllerMode {
case stickers
@ -565,7 +564,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv
}
}
let text = packNames.map { "https://t.me/addstickers/\($0)" }.joined(separator: "\n")
let shareController = ShareController(context: context, subject: .text(text), externalShare: true)
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .text(text), externalShare: true))
presentControllerImpl?(shareController, nil)
})])
} else {

View file

@ -14,7 +14,7 @@ import StickerPackPreviewUI
import ItemListStickerPackItem
import ItemListPeerActionItem
import UndoUI
import ShareController
import WebPBinding
import ReactionImageComponent
import FeaturedStickersScreen
@ -1063,7 +1063,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta
}
}
let text = packNames.map { "https://t.me/addstickers/\($0)" }.joined(separator: "\n")
let shareController = ShareController(context: context, subject: .text(text), externalShare: true)
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .text(text), externalShare: true))
presentControllerImpl?(shareController, nil)
})])
} else {

View file

@ -13,7 +13,7 @@ import AlertUI
import PresentationDataUtils
import MediaResources
import WallpaperResources
import ShareController
import AccountContext
import ContextUI
import UndoUI
@ -623,11 +623,10 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c?.dismiss(completion: {
let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default)
shareController.actionCompleted = {
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
})
})))
@ -876,11 +875,10 @@ public func themePickerController(context: AccountContext, focusOnItemTag: Theme
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c?.dismiss(completion: {
let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(cloudTheme.theme.slug)"), preferredAction: .default)
shareController.actionCompleted = {
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url("https://t.me/addtheme/\(cloudTheme.theme.slug)"), preferredAction: .default, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
})
})))

View file

@ -8,7 +8,6 @@ import TelegramCore
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
import ShareController
import CounterControllerTitleView
import WallpaperResources
import OverlayStatusController
@ -524,7 +523,7 @@ public final class ThemePreviewController: ViewController {
subject = .media(media, nil)
preferredAction = .default
}
let controller = ShareController(context: self.context, subject: subject, preferredAction: preferredAction)
let controller = self.context.sharedContext.makeShareController(context: self.context, params: ShareControllerParams(subject: subject, preferredAction: preferredAction))
self.present(controller, in: .window(.root), blockInteraction: true)
}
}

View file

@ -13,7 +13,7 @@ import AlertUI
import PresentationDataUtils
import MediaResources
import WallpaperResources
import ShareController
import AccountContext
import ContextUI
import UndoUI
@ -776,11 +776,10 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c?.dismiss(completion: {
let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default)
shareController.actionCompleted = {
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url("https://t.me/addtheme/\(theme.theme.slug)"), preferredAction: .default, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
})
})))
@ -1027,11 +1026,10 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
}
items.append(.action(ContextMenuActionItem(text: presentationData.strings.Appearance_ShareTheme, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Share"), color: theme.contextMenu.primaryColor) }, action: { c, f in
c?.dismiss(completion: {
let shareController = ShareController(context: context, subject: .url("https://t.me/addtheme/\(cloudTheme.theme.slug)"), preferredAction: .default)
shareController.actionCompleted = {
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url("https://t.me/addtheme/\(cloudTheme.theme.slug)"), preferredAction: .default, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
})
})))

View file

@ -7,7 +7,6 @@ import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import AccountContext
import ShareController
import UndoUI
import InviteLinksUI
import TextFormat
@ -484,11 +483,10 @@ public func usernameSetupController(context: AccountContext, mode: UsernameSetup
}
if !currentAddressName.isEmpty {
dismissInputImpl?()
let shareController = ShareController(context: context, subject: .url("https://t.me/\(currentAddressName)"))
shareController.actionCompleted = {
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url("https://t.me/\(currentAddressName)"), actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), nil)
}
}))
presentControllerImpl?(shareController, nil)
}
}

View file

@ -25,36 +25,6 @@ import ChatMessagePaymentAlertController
private var ObjCKey_DeinitWatcher: Int?
public struct ShareControllerAction {
let title: String
let action: () -> Void
public init(title: String, action: @escaping () -> Void) {
self.title = title
self.action = action
}
}
public enum ShareControllerPreferredAction {
case `default`
case saveToCameraRoll
case custom(action: ShareControllerAction)
}
public struct ShareControllerSegmentedValue {
let title: String
let subject: ShareControllerSubject
let actionTitle: String
let formatSendTitle: (Int) -> String
public init(title: String, subject: ShareControllerSubject, actionTitle: String, formatSendTitle: @escaping (Int) -> String) {
self.title = title
self.subject = subject
self.actionTitle = actionTitle
self.formatSendTitle = formatSendTitle
}
}
private enum ExternalShareItem {
case text(String)
case url(URL)
@ -327,43 +297,6 @@ public final class ShareControllerAppEnvironment: ShareControllerEnvironment {
}
}
public final class ShareControllerAppAccountContext: ShareControllerAccountContext {
public let context: AccountContext
public var accountId: AccountRecordId {
return self.context.account.id
}
public var accountPeerId: EnginePeer.Id {
return self.context.account.stateManager.accountPeerId
}
public var stateManager: AccountStateManager {
return self.context.account.stateManager
}
public var engineData: TelegramEngine.EngineData {
return self.context.engine.data
}
public var animationCache: AnimationCache {
return self.context.animationCache
}
public var animationRenderer: MultiAnimationRenderer {
return self.context.animationRenderer
}
public var contentSettings: ContentSettings {
return self.context.currentContentSettings.with { $0 }
}
public var appConfiguration: AppConfiguration {
return self.context.currentAppConfiguration.with { $0 }
}
public init(context: AccountContext) {
self.context = context
}
public func resolveInlineStickers(fileIds: [Int64]) -> Signal<[Int64: TelegramMediaFile], NoError> {
return self.context.engine.stickers.resolveInlineStickers(fileIds: fileIds)
}
}
public final class ShareControllerSwitchableAccount: Equatable {
public let account: ShareControllerAccountContext
public let peer: Peer

View file

@ -18,7 +18,7 @@ import ContextUI
import ItemListPeerItem
import InviteLinksUI
import UndoUI
import ShareController
import ItemListPeerActionItem
import PremiumUI
import StoryContainerScreen
@ -1940,8 +1940,10 @@ public func channelStatsController(
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
}, shareBoostLink: { link in
let shareController = ShareController(context: context, subject: .url(link), updatedPresentationData: updatedPresentationData)
shareController.completed = { peerIds in
let shareController = context.sharedContext.makeShareController(context: context, params: ShareControllerParams(subject: .url(link), updatedPresentationData: updatedPresentationData, actionCompleted: {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
}, completed: { peerIds in
let _ = (context.engine.data.get(
EngineDataList(
peerIds.map(TelegramEngine.EngineData.Item.Peer.Peer.init)
@ -1950,7 +1952,7 @@ public func channelStatsController(
|> deliverOnMainQueue).start(next: { peerList in
let peers = peerList.compactMap { $0 }
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let text: String
var savedMessages = false
if peerIds.count == 1, let peerId = peerIds.first, peerId == context.account.peerId {
@ -1971,7 +1973,7 @@ public func channelStatsController(
text = ""
}
}
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: savedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { action in
if savedMessages, action == .info {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
@ -1985,11 +1987,7 @@ public func channelStatsController(
return false
}))
})
}
shareController.actionCompleted = {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
presentImpl?(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.ChannelBoost_BoostLinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }))
}
}))
presentImpl?(shareController)
},
openBoost: { boost in

View file

@ -1110,16 +1110,16 @@ private final class StickerPackContainer: ASDisplayNode {
let parentNavigationController = strongSelf.controller?.parentNavigationController
let shareController = strongSelf.context.sharedContext.makeShareController(
context: strongSelf.context,
subject: shareSubject,
forceExternal: false,
shareStory: nil,
enqueued: nil,
actionCompleted: { [weak parentNavigationController] in
if let parentNavigationController = parentNavigationController, let controller = parentNavigationController.topViewController as? ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
params: ShareControllerParams(
subject: shareSubject,
externalShare: false,
actionCompleted: { [weak parentNavigationController] in
if let parentNavigationController = parentNavigationController, let controller = parentNavigationController.topViewController as? ViewController {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
controller.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.Conversation_LinkCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}
}
)
)
strongSelf.controller?.present(shareController, in: .window(.root))
}

View file

@ -228,6 +228,7 @@ public protocol ManagedAudioSession: AnyObject {
func isActive() -> Signal<Bool, NoError>
func isPlaybackActive() -> Signal<Bool, NoError>
func isOtherAudioPlaying() -> Bool
func didActivateWithZeroVolume() -> Signal<Void, NoError>
func push(params: ManagedAudioSessionClientParams) -> Disposable
func dropAll()
@ -311,6 +312,11 @@ public final class ManagedAudioSessionImpl: NSObject, ManagedAudioSession {
private var isActiveValue: Bool = false
private var callKitAudioSessionIsActive: Bool = false
private let zeroVolumeEvents = ValuePipe<Void>()
public func didActivateWithZeroVolume() -> Signal<Void, NoError> {
return self.zeroVolumeEvents.signal()
}
override public init() {
self.queue = Queue()
@ -1123,6 +1129,22 @@ public final class ManagedAudioSessionImpl: NSObject, ManagedAudioSession {
if case .voiceCall = type {
//try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(0.005)
}
if AVAudioSession.sharedInstance().outputVolume <= 0.01 {
let queue = self.queue
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1, execute: { [weak self] in
queue.async {
guard let self else {
return
}
if self.currentTypeAndOutputMode != nil {
if AVAudioSession.sharedInstance().outputVolume <= 0.01 {
self.zeroVolumeEvents.putNext(Void())
}
}
}
})
}
} catch let error {
managedAudioSessionLog("ManagedAudioSession activate error \(error)")
}

View file

@ -7,7 +7,7 @@ import SwiftSignalKit
import AVKit
import TelegramCore
import Postbox
import ShareController
import UndoUI
import TelegramPresentationData
import PresentationDataUtils
@ -1202,8 +1202,12 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
var segmentedValues: [ShareControllerSegmentedValue]?
segmentedValues = nil
let shareController = ShareController(context: strongSelf.context, subject: .url(inviteLinks.listenerLink), segmentedValues: segmentedValues, forceTheme: defaultDarkPresentationTheme, forcedActionTitle: presentationData.strings.VoiceChat_CopyInviteLink)
shareController.completed = { [weak self] peerIds in
let shareController = strongSelf.context.sharedContext.makeShareController(context: strongSelf.context, params: ShareControllerParams(subject: .url(inviteLinks.listenerLink), segmentedValues: segmentedValues, forceTheme: defaultDarkPresentationTheme, forcedActionTitle: presentationData.strings.VoiceChat_CopyInviteLink, actionCompleted: { [weak self] in
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.VoiceChat_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}, completed: { [weak self] peerIds in
if let strongSelf = self {
let _ = (strongSelf.context.engine.data.get(
EngineDataList(
@ -1214,7 +1218,7 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
if let strongSelf = self {
let peers = peerList.compactMap { $0 }
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
let text: String
var isSavedMessages = false
if peers.count == 1, let peer = peers.first {
@ -1231,18 +1235,12 @@ public final class MediaStreamComponentController: ViewControllerComponentContai
} else {
text = ""
}
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: isSavedMessages, text: text), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .current)
}
})
}
}
shareController.actionCompleted = {
if let strongSelf = self {
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .linkCopied(title: nil, text: presentationData.strings.VoiceChat_InviteLinkCopiedText), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .window(.root))
}
}
}))
strongSelf.present(shareController, in: .window(.root))
}
})

View file

@ -18,7 +18,7 @@ import DeviceAccess
import TelegramVoip
import PresentationDataUtils
import UndoUI
import ShareController
import AvatarNode
import TelegramAudio
import LegacyComponents
@ -825,8 +825,13 @@ final class VideoChatScreenComponent: Component {
return formatSendTitle(environment.strings.VoiceChat_InviteLink_InviteListeners(Int32(count)))
})]
}
let shareController = ShareController(context: groupCall.accountContext, subject: .url(inviteLinks.listenerLink), segmentedValues: segmentedValues, forceTheme: environment.theme, forcedActionTitle: environment.strings.VoiceChat_CopyInviteLink)
shareController.completed = { [weak self] peerIds in
let shareController = groupCall.accountContext.sharedContext.makeShareController(context: groupCall.accountContext, params: ShareControllerParams(subject: .url(inviteLinks.listenerLink), segmentedValues: segmentedValues, forceTheme: environment.theme, forcedActionTitle: environment.strings.VoiceChat_CopyInviteLink, actionCompleted: { [weak self] in
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return
}
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
self.presentToast(icon: .animation("anim_linkcopied"), text: presentationData.strings.VoiceChat_InviteLinkCopiedText, duration: 3)
}, completed: { [weak self] peerIds in
guard let self, case let .group(groupCall) = self.currentCall else {
return
}
@ -839,10 +844,10 @@ final class VideoChatScreenComponent: Component {
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return
}
let peers = peerList.compactMap { $0 }
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
let text: String
var isSavedMessages = false
if peers.count == 1, let peer = peers.first {
@ -861,14 +866,7 @@ final class VideoChatScreenComponent: Component {
}
self.presentToast(icon: .animation(isSavedMessages ? "anim_savedmessages" : "anim_forward"), text: text, duration: 3)
})
}
shareController.actionCompleted = { [weak self] in
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return
}
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
self.presentToast(icon: .animation("anim_linkcopied"), text: presentationData.strings.VoiceChat_InviteLinkCopiedText, duration: 3)
}
}))
environment.controller()?.present(shareController, in: .window(.root))
})
} else if groupCall.isConference {
@ -876,8 +874,13 @@ final class VideoChatScreenComponent: Component {
return
}
let shareController = ShareController(context: groupCall.accountContext, subject: .url(inviteLinks.listenerLink), forceTheme: environment.theme, forcedActionTitle: environment.strings.VoiceChat_CopyInviteLink)
shareController.completed = { [weak self] peerIds in
let shareController = groupCall.accountContext.sharedContext.makeShareController(context: groupCall.accountContext, params: ShareControllerParams(subject: .url(inviteLinks.listenerLink), forceTheme: environment.theme, forcedActionTitle: environment.strings.VoiceChat_CopyInviteLink, actionCompleted: { [weak self] in
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return
}
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
self.presentToast(icon: .animation("anim_linkcopied"), text: presentationData.strings.VoiceChat_InviteLinkCopiedText, duration: 3)
}, completed: { [weak self] peerIds in
guard let self, case let .group(groupCall) = self.currentCall else {
return
}
@ -890,10 +893,10 @@ final class VideoChatScreenComponent: Component {
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return
}
let peers = peerList.compactMap { $0 }
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
let text: String
var isSavedMessages = false
if peers.count == 1, let peer = peers.first {
@ -912,14 +915,7 @@ final class VideoChatScreenComponent: Component {
}
self.presentToast(icon: .animation(isSavedMessages ? "anim_savedmessages" : "anim_forward"), text: text, duration: 3)
})
}
shareController.actionCompleted = { [weak self] in
guard let self, let environment = self.environment, case let .group(groupCall) = self.currentCall else {
return
}
let presentationData = groupCall.accountContext.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: environment.theme)
self.presentToast(icon: .animation("anim_linkcopied"), text: presentationData.strings.VoiceChat_InviteLinkCopiedText, duration: 3)
}
}))
environment.controller()?.present(shareController, in: .window(.root))
}
}

View file

@ -15,7 +15,6 @@ import MergeLists
import ItemListUI
import AppBundle
import ContextUI
import ShareController
import DeleteChatPeerActionSheetItem
import UndoUI
import AlertUI

View file

@ -10,9 +10,9 @@ import TelegramUIPreferences
import AccountContext
import AlertUI
import PresentationDataUtils
import ShareController
import AvatarNode
import UndoUI
import ShareController
public final class VoiceChatJoinScreen: ViewController {
private var controllerNode: Node {

View file

@ -480,11 +480,7 @@ private func synchronizeUnseenReactionsAndPollVotesTag(postbox: Postbox, network
return postbox.transaction { transaction -> Void in
transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: .unseenReaction, namespace: entry.key.namespace, customTag: nil, count: apiUnreadReactionsCount, maxId: apiTopMessage)
#if DEBUG
#else
//let _ = this_is_an_error
transaction.replaceMessageTagSummary(peerId: entry.key.peerId, threadId: nil, tagMask: .unseenPollVote, namespace: entry.key.namespace, customTag: nil, count: apiUnreadPollVoteCount, maxId: apiTopMessage)
#endif
}
} else {
return .complete()

View file

@ -58,8 +58,8 @@ public extension TelegramEngine {
return _internal_searchPeers(accountPeerId: self.account.peerId, postbox: self.account.postbox, network: self.account.network, query: query, scope: scope)
}
public func searchLocalPeers(query: String, scope: TelegramSearchPeersScope = .everywhere) -> Signal<[EngineRenderedPeer], NoError> {
return self.account.postbox.searchPeers(query: query)
public func searchLocalPeers(query: String, scope: TelegramSearchPeersScope = .everywhere, predicate: ChatListFilterPredicate? = nil) -> Signal<[EngineRenderedPeer], NoError> {
return self.account.postbox.searchPeers(query: query, predicate: predicate)
|> map { peers in
switch scope {
case .everywhere:

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