mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
4da24b44dc
93 changed files with 2914 additions and 1410 deletions
|
|
@ -1,7 +1,7 @@
|
|||
http_file = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
|
||||
|
||||
bazel_dep(name = "bazel_features", version = "1.30.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.7.1")
|
||||
bazel_dep(name = "bazel_features", version = "1.33.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.8.1")
|
||||
bazel_dep(name = "platforms", version = "0.0.11")
|
||||
|
||||
bazel_dep(name = "rules_xcodeproj")
|
||||
|
|
|
|||
165
MODULE.bazel.lock
generated
165
MODULE.bazel.lock
generated
|
|
@ -9,7 +9,10 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20240116.2/MODULE.bazel": "73939767a4686cd9a520d16af5ab440071ed75cec1a876bf2fcfaf1f71987a16",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20250127.1/MODULE.bazel": "c4a89e7ceb9bf1e25cf84a9f830ff6b817b72874088bf5141b314726e46a57c1",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20250512.1/MODULE.bazel": "d209fdb6f36ffaf61c509fcc81b19e81b411a999a934a032e10cd009a0226215",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20250512.1/source.json": "d725d73707d01bb46ab3ca59ba408b8e9bd336642ca77a2269d4bfb8bbfd413d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d",
|
||||
|
|
@ -21,9 +24,9 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.33.0/MODULE.bazel": "8b8dc9d2a4c88609409c3191165bccec0e4cb044cd7a72ccbe826583303459f6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.33.0/source.json": "13617db3930328c2cd2807a0f13d52ca870ac05f96db9668655113265147b2a6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
|
||||
|
|
@ -36,19 +39,23 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.8.1/MODULE.bazel": "88ade7293becda963e0e3ea33e7d54d3425127e0a326e0d17da085a5f1f03ff6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.8.1/source.json": "7ebaefba0b03efe59cac88ed5bbc67bcf59a3eff33af937345ede2a38b2d368a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.15.2/MODULE.bazel": "6de1edc1d26cafb0ea1a6ab3f4d4192d91a312fd2d360b63adaa213cd00b2108",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.17.0/MODULE.bazel": "dbec758171594a705933a29fcf69293d2468c49ec1f2ebca65c36f504d72df46",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/googletest/1.17.0/source.json": "38e4454b25fc30f15439c0378e57909ab1fd0a443158aa35aec685da727cd713",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/jsoncpp/1.9.6/MODULE.bazel": "2f8d20d3b7d54143213c4dfc3d98225c42de7d666011528dc8fe91591e2e17b0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/jsoncpp/1.9.6/source.json": "a04756d367a2126c3541682864ecec52f92cdee80a35735a3cb249ce015ca000",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/nlohmann_json/3.12.0.bcr.1/MODULE.bazel": "a1c8bb07b5b91d971727c635f449d05623ac9608f6fe4f5f04254ea12f08e349",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/nlohmann_json/3.12.0.bcr.1/source.json": "93f82a5ae985eb935c539bfee95e04767187818189241ac956f3ccadbdb8fb02",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
|
||||
|
|
@ -64,12 +71,17 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/29.1/MODULE.bazel": "557c3457560ff49e122ed76c0bc3397a64af9574691cb8201b4e46d4ab2ecb95",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/33.4/MODULE.bazel": "114775b816b38b6d0ca620450d6b02550c60ceedfdc8d9a229833b34a223dc42",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/33.4/source.json": "555f8686b4c7d6b5ba731fbea13bf656b4bfd9a7ff629c1d9d3f6e1d6155de79",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/pybind11_bazel/2.12.0/MODULE.bazel": "e6f4c20442eaa7c90d7190d8dc539d0ab422f95c65a57cc59562170c58ae3d34",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/pybind11_bazel/2.12.0/source.json": "6900fdc8a9e95866b8c0d4ad4aba4d4236317b5c1cd04c502df3f0d33afed680",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2024-07-02.bcr.1/MODULE.bazel": "b4963dda9b31080be1905ef085ecd7dd6cd47c05c79b9cdf83ade83ab2ab271a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2024-07-02.bcr.1/source.json": "2ff292be6ef3340325ce8a045ecc326e92cbfab47c7cbab4bd85d28971b97ac4",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/re2/2024-07-02/MODULE.bazel": "0eadc4395959969297cbcf31a249ff457f2f1d456228c67719480205aa306daa",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
|
||||
|
|
@ -78,16 +90,18 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.17/MODULE.bazel": "2ae1d8f4238ec67d7185d8861cb0a2cdf4bc608697c331b95bf990e69b62e64a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.1.2/source.json": "53fcb09b5816c83ca60d9d7493faf3bfaf410dfc2f15deb52d6ddd146b8d43f0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.2.14/MODULE.bazel": "353c99ed148887ee89c54a17d4100ae7e7e436593d104b668476019023b58df8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.2.15/MODULE.bazel": "6a0a4a75a57aa6dc888300d848053a58c6b12a29f89d4304e1c41448514ec6e8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.2.15/source.json": "197965c6dcca5c98a9288f93849e2e1c69d622e71b0be8deb524e22d48c88e32",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
|
||||
|
|
@ -102,13 +116,15 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.7/MODULE.bazel": "e717beabc4d091ecb2c803c2d341b88590e9116b8bf7947915eeb33aab4f96dd",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_jvm_external/6.7/source.json": "5426f412d0a7fc6b611643376c7e4a82dec991491b9ce5cb1cfdd25fe2e92be4",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5",
|
||||
|
|
@ -121,19 +137,23 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/7.1.0/MODULE.bazel": "002d62d9108f75bb807cd56245d45648f38275cb3a99dcd45dfb864c5d74cb96",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_proto/7.1.0/source.json": "39f89066c12c24097854e8f57ab8558929f9c8d474d34b2c00ac04630ad8940e",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.33.2/MODULE.bazel": "3e036c4ad8d804a4dad897d333d8dce200d943df4827cb849840055be8d2e937",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/1.3.0/source.json": "25932f917cd279c7baefa6cb1d3fa8750a7a29de522024449b19af6eab51f4a0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/1.6.0/MODULE.bazel": "7e04ad8f8d5bea40451cf80b1bd8262552aa73f841415d20db96b7241bd027d8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_python/1.6.0/source.json": "e980f654cf66ec4928672f41fc66c4102b5ea54286acf4aecd23256c84211be6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3",
|
||||
|
|
@ -144,8 +164,8 @@
|
|||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/swift_argument_parser/1.3.1.2/MODULE.bazel": "75aab2373a4bbe2a1260b9bf2a1ebbdbf872d3bd36f80bff058dccd82e89422f",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/swift_argument_parser/1.3.1.2/source.json": "5fba48bbe0ba48761f9e9f75f92876cafb5d07c0ce059cc7a8027416de94a05b",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
|
||||
|
|
@ -220,8 +240,8 @@
|
|||
},
|
||||
"@@rules_python+//python/uv:uv.bzl%uv": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "Xpqjnjzy6zZ90Es9Wa888ZLHhn7IsNGbph/e6qoxzw8=",
|
||||
"usagesDigest": "vJ5RHUxAnV24M5swNGiAnkdxMx3Hp/iOLmNANTC5Xc8=",
|
||||
"bzlTransitiveDigest": "PmZM/pIkZKEDDL68TohlKJrWPYKL5VwUw3MA7kmm6fk=",
|
||||
"usagesDigest": "p80sy6cYQuWxx5jhV3fOTu+N9EyIUFG9+F7UC/nhXic=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
|
|
@ -246,6 +266,11 @@
|
|||
}
|
||||
},
|
||||
"recordedRepoMappingEntries": [
|
||||
[
|
||||
"rules_python+",
|
||||
"bazel_tools",
|
||||
"bazel_tools"
|
||||
],
|
||||
[
|
||||
"rules_python+",
|
||||
"platforms",
|
||||
|
|
@ -256,7 +281,7 @@
|
|||
},
|
||||
"@@rules_xcodeproj+//xcodeproj:extensions.bzl%internal": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "hsJI1bzu5CX3ZE+NX8jnNWEOh1pNDT650SbTSBnF5jA=",
|
||||
"bzlTransitiveDigest": "+qmLBZzimJ0CYyKoQg6/pbdkTnu/s4e5IisoM+TLM+8=",
|
||||
"usagesDigest": "fvsnMonVwKDYnBfww4bXuYie3WU0d9VSqT2gePSdQco=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
|
|
@ -268,93 +293,21 @@
|
|||
}
|
||||
},
|
||||
"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_tools",
|
||||
"bazel_tools"
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"@@rules_xcodeproj+//xcodeproj:extensions.bzl%non_module_deps": {
|
||||
"general": {
|
||||
"bzlTransitiveDigest": "hsJI1bzu5CX3ZE+NX8jnNWEOh1pNDT650SbTSBnF5jA=",
|
||||
"usagesDigest": "jzxYhnOC9BE0dJ0biFLfxWXi/+R19uAAZkJ0p9CY0JI=",
|
||||
"recordedFileInputs": {},
|
||||
"recordedDirentsInputs": {},
|
||||
"envVariables": {},
|
||||
"generatedRepoSpecs": {
|
||||
"rules_xcodeproj_legacy_index_import": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "load(\"@bazel_skylib//rules:native_binary.bzl\", \"native_binary\")\n\nnative_binary(\n name = \"index_import\",\n src = \"index-import\",\n out = \"index-import\",\n visibility = [\"//visibility:public\"],\n)\n",
|
||||
"canonical_id": "index-import-5.8.0.1",
|
||||
"sha256": "28c1ffa39d99e74ed70623899b207b41f79214c498c603915aef55972a851a15",
|
||||
"url": "https://github.com/MobileNativeFoundation/index-import/releases/download/5.8.0.1/index-import.tar.gz"
|
||||
}
|
||||
},
|
||||
"rules_xcodeproj_index_import": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "load(\"@bazel_skylib//rules:native_binary.bzl\", \"native_binary\")\n\nnative_binary(\n name = \"index_import\",\n src = \"index-import\",\n out = \"index-import\",\n visibility = [\"//visibility:public\"],\n)\n",
|
||||
"canonical_id": "index-import-6.1.0.1",
|
||||
"sha256": "9a54fc1674af6031125a9884480a1e31e1bcf48b8f558b3e8bcc6b6fcd6e8b61",
|
||||
"url": "https://github.com/MobileNativeFoundation/index-import/releases/download/6.1.0.1/index-import.tar.gz"
|
||||
}
|
||||
},
|
||||
"com_github_apple_swift_argument_parser": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "load(\"@build_bazel_rules_swift//swift:swift.bzl\", \"swift_library\")\n\nswift_library(\n name = \"ArgumentParserToolInfo\",\n srcs = glob([\"Sources/ArgumentParserToolInfo/**/*.swift\"]),\n visibility = [\"//visibility:public\"],\n)\n\nswift_library(\n name = \"ArgumentParser\",\n srcs = glob([\"Sources/ArgumentParser/**/*.swift\"]),\n visibility = [\"//visibility:public\"],\n deps = [\":ArgumentParserToolInfo\"],\n)\n",
|
||||
"sha256": "4a10bbef290a2167c5cc340b39f1f7ff6a8cf4e1b5433b68548bf5f1e542e908",
|
||||
"strip_prefix": "swift-argument-parser-1.2.3",
|
||||
"url": "https://github.com/apple/swift-argument-parser/archive/refs/tags/1.2.3.tar.gz"
|
||||
}
|
||||
},
|
||||
"com_github_michaeleisel_jjliso8601dateformatter": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "objc_library(\n name = \"JJLISO8601DateFormatter\",\n srcs = glob([\"Sources/JJLISO8601DateFormatter/**/*\"]),\n copts = [\n \"-Wno-incompatible-pointer-types\",\n \"-Wno-incompatible-pointer-types-discards-qualifiers\",\n \"-Wno-shorten-64-to-32\",\n \"-Wno-unreachable-code\",\n \"-Wno-unused-function\",\n \"-Wno-unused-variable\",\n ],\n includes = [\"Sources/JJLISO8601DateFormatter/include\"],\n hdrs = glob([\"Sources/JJLISO8601DateFormatter/include/*\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
||||
"patches": [
|
||||
"@@rules_xcodeproj+//third_party/com_github_michaeleisel_jjliso8601dateformatter:include_fix.patch"
|
||||
],
|
||||
"sha256": "6fe15f251f100f3df057c2802a50765387674fde9c922375683682b5ba37eef0",
|
||||
"strip_prefix": "JJLISO8601DateFormatter-0.1.6",
|
||||
"url": "https://github.com/michaeleisel/JJLISO8601DateFormatter/archive/refs/tags/0.1.6.tar.gz"
|
||||
}
|
||||
},
|
||||
"com_github_michaeleisel_zippyjsoncfamily": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "objc_library(\n name = \"ZippyJSONCFamily\",\n copts = [\n \"-std=c++17\",\n \"-Wno-unused-function\",\n \"-Wno-reorder-ctor\",\n \"-Wno-return-type-c-linkage\",\n \"-Wno-shorten-64-to-32\",\n \"-Wno-unused-variable\",\n ],\n srcs = glob([\"Sources/ZippyJSONCFamily/**/*\"]),\n includes = [\"Sources/ZippyJSONCFamily/include\"],\n hdrs = glob([\"Sources/ZippyJSONCFamily/include/*\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
||||
"patches": [
|
||||
"@@rules_xcodeproj+//third_party/com_github_michaeleisel_zippyjsoncfamily:include_fix.patch"
|
||||
],
|
||||
"sha256": "b215927ada8403e1b056d39450c6a7b59122eca4b0c7fc5beb5f0b5fea2acd72",
|
||||
"strip_prefix": "ZippyJSONCFamily-1.2.9",
|
||||
"url": "https://github.com/michaeleisel/ZippyJSONCFamily/archive/refs/tags/1.2.9.tar.gz"
|
||||
}
|
||||
},
|
||||
"com_github_michaeleisel_zippyjson": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "load(\"@build_bazel_rules_swift//swift:swift.bzl\", \"swift_library\")\n\nswift_library(\n name = \"ZippyJSON\",\n srcs = glob([\"Sources/ZippyJSON/**/*.swift\"]),\n deps = [\n \"@com_github_michaeleisel_jjliso8601dateformatter//:JJLISO8601DateFormatter\",\n \"@com_github_michaeleisel_zippyjsoncfamily//:ZippyJSONCFamily\",\n ],\n visibility = [\"//visibility:public\"],\n)\n",
|
||||
"sha256": "4b256843c9c3686c527e76dde54f8d76b6201c1fd903c07dc2211ab1b250bd04",
|
||||
"strip_prefix": "ZippyJSON-1.2.10",
|
||||
"url": "https://github.com/michaeleisel/ZippyJSON/archive/refs/tags/1.2.10.tar.gz"
|
||||
}
|
||||
},
|
||||
"com_github_apple_swift_collections": {
|
||||
"repoRuleId": "@@bazel_tools//tools/build_defs/repo:http.bzl%http_archive",
|
||||
"attributes": {
|
||||
"build_file_content": "load(\"@build_bazel_rules_swift//swift:swift.bzl\", \"swift_library\")\n\nswift_library(\n name = \"Collections\",\n srcs = glob([\"Sources/Collections/**/*.swift\"]),\n deps = [\n \"@com_github_apple_swift_collections//:DequeModule\",\n ],\n visibility = [\"//visibility:public\"],\n)\n\nswift_library(\n name = \"DequeModule\",\n srcs = glob([\"Sources/DequeModule/**/*.swift\"]),\n visibility = [\"//visibility:public\"],\n)\n\nswift_library(\n name = \"OrderedCollections\",\n srcs = glob([\"Sources/OrderedCollections/**/*.swift\"]),\n visibility = [\"//visibility:public\"],\n)\n",
|
||||
"sha256": "1a2ec8cc6c63c383a9dd4eb975bf83ce3bc7a2ac21a0289a50dae98a576327d6",
|
||||
"strip_prefix": "swift-collections-4cab1c1c417855b90e9cfde40349a43aff99c536",
|
||||
"url": "https://github.com/apple/swift-collections/archive/4cab1c1c417855b90e9cfde40349a43aff99c536.tar.gz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recordedRepoMappingEntries": [
|
||||
"bazel_features",
|
||||
"bazel_features+"
|
||||
],
|
||||
[
|
||||
"rules_xcodeproj+",
|
||||
"bazel_tools",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ load("@build_bazel_rules_apple//apple:ios.bzl",
|
|||
"ios_application",
|
||||
"ios_extension",
|
||||
"ios_framework",
|
||||
"ios_ui_test",
|
||||
"ios_ui_test_suite",
|
||||
)
|
||||
|
||||
load(
|
||||
"@build_bazel_rules_apple//apple/testing/default_runner:ios_test_runner.bzl",
|
||||
"ios_test_runner",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_apple//apple:resources.bzl",
|
||||
|
|
@ -21,6 +28,7 @@ load("@build_bazel_rules_swift//swift:swift.bzl",
|
|||
load(
|
||||
"@rules_xcodeproj//xcodeproj:defs.bzl",
|
||||
"top_level_targets",
|
||||
"top_level_target",
|
||||
"xcodeproj",
|
||||
"xcode_provisioning_profile",
|
||||
)
|
||||
|
|
@ -1759,12 +1767,10 @@ xcodeproj(
|
|||
bazel_path = telegram_bazel_path,
|
||||
project_name = "Telegram",
|
||||
tags = ["manual"],
|
||||
top_level_targets = top_level_targets(
|
||||
labels = [
|
||||
":Telegram",
|
||||
],
|
||||
target_environments = ["device", "simulator"],
|
||||
),
|
||||
top_level_targets = [
|
||||
top_level_target(":Telegram", target_environments = ["device", "simulator"]),
|
||||
":iOSAppUITestSuite",
|
||||
],
|
||||
xcode_configurations = {
|
||||
"Debug": {
|
||||
"//command_line_option:compilation_mode": "dbg",
|
||||
|
|
@ -1776,6 +1782,39 @@ xcodeproj(
|
|||
default_xcode_configuration = "Debug"
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "TestSources",
|
||||
srcs = glob(["Tests/Sources/**/*.swift"]),
|
||||
)
|
||||
|
||||
ios_test_runner(
|
||||
name = "iPhone-17__26.2",
|
||||
device_type = "iPhone 17",
|
||||
os_version = "26.2",
|
||||
)
|
||||
|
||||
|
||||
swift_library(
|
||||
name = "iOSAppUITestSuite.library",
|
||||
testonly = True,
|
||||
srcs = [":TestSources"],
|
||||
module_name = "iOSAppUITestSuite",
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
ios_ui_test_suite(
|
||||
name = "iOSAppUITestSuite",
|
||||
bundle_id = "org.telegram.Telegram-iOS-uitests",
|
||||
minimum_os_version = "13.0",
|
||||
runners = [
|
||||
":iPhone-17__26.2",
|
||||
],
|
||||
tags = ["manual"],
|
||||
test_host = "//Telegram",
|
||||
visibility = ["@rules_xcodeproj//xcodeproj:generated"],
|
||||
deps = [":iOSAppUITestSuite.library"],
|
||||
)
|
||||
|
||||
# Temporary targets used to simplify build tests
|
||||
|
||||
ios_application(
|
||||
|
|
|
|||
|
|
@ -15676,9 +15676,9 @@ Error: %8$@";
|
|||
|
||||
"Gift.Craft.Title" = "Craft Gift";
|
||||
"Gift.Craft.Description" = "Add up to **4 gifts** to craft new\n**$ %@**.\n\nIf crafting fails, all used gifts\nwill be lost.";
|
||||
"Gift.Craft.ViewVariants" = "View all new variants";
|
||||
"Gift.Craft.BackdropTooltip" = "There's **%1$@%** chance the crafted gift will have **%2$@** backdrop.";
|
||||
"Gift.Craft.SymbolTooltip" = "There's **%1$@%** chance the crafted gift will have **%2$@** symbol.";
|
||||
"Gift.Craft.ViewVariants" = "View all craftable models";
|
||||
"Gift.Craft.BackdropTooltip" = "**%1$@%** chance the crafted gift will have **%2$@** backdrop.";
|
||||
"Gift.Craft.SymbolTooltip" = "**%1$@%** chance the crafted gift will have **%2$@** symbol.";
|
||||
"Gift.Craft.SuccessChanceSuffix" = "% Success Chance";
|
||||
"Gift.Craft.Craft" = "Craft %@";
|
||||
"Gift.Craft.Crafting.Title" = "Crafting";
|
||||
|
|
@ -15699,6 +15699,7 @@ Error: %8$@";
|
|||
"Gift.Craft.Info.Understood" = "Understood";
|
||||
"Gift.Craft.Unavailable.Title" = "Try Later";
|
||||
"Gift.Craft.Unavailable.Text" = "You will be able to craft this gift on %@.";
|
||||
"Gift.Craft.Error.NotAvailable" = "One of the gifts is not available for crafting.";
|
||||
|
||||
"Gift.Craft.Select.Title" = "Select Gifts";
|
||||
"Gift.Craft.Select.YourGifts" = "Your Gifts";
|
||||
|
|
@ -15721,3 +15722,36 @@ Error: %8$@";
|
|||
"Gift.Variants.ViewPrimaryModels" = "View Primary Models";
|
||||
|
||||
"Gift.View.OnSale" = "On sale for %@";
|
||||
|
||||
"Gift.Store.Balance.MyStars" = "My Stars";
|
||||
"Gift.Store.Balance.MyTon" = "My TON";
|
||||
|
||||
"Resolve.GiftErrorNotFound" = "Sorry, this gift doesn't seem to exist.";
|
||||
"Resolve.GiftErrorBurned" = "Sorry, this gift has already been burned.";
|
||||
|
||||
"Notification.StarGift.Burned" = "burned";
|
||||
|
||||
"AuthConfirmation.Title" = "Log in to **%@**";
|
||||
"AuthConfirmation.Description" = "This site will receive your **name**,\n**username** and **profile photo**.";
|
||||
"AuthConfirmation.Device" = "Device";
|
||||
"AuthConfirmation.IpAddress" ="IP Address";
|
||||
"AuthConfirmation.Info" ="This login attempt came from the device above.";
|
||||
"AuthConfirmation.AllowMessages" = "Allow Messages";
|
||||
"AuthConfirmation.AllowMessagesInfo" = "This will allow %@ to message you.";
|
||||
"AuthConfirmation.Cancel" = "Cancel";
|
||||
"AuthConfirmation.LogIn" = "Log In";
|
||||
|
||||
"AuthConfirmation.PhoneNumberConfirmation.Title" = "Phone Number";
|
||||
"AuthConfirmation.PhoneNumberConfirmation.Text" = "**%1$@** wants to access your phone number **%2$@**.\n\nAllow access?";
|
||||
"AuthConfirmation.PhoneNumberConfirmation.Deny" = "Deny";
|
||||
"AuthConfirmation.PhoneNumberConfirmation.Allow" = "Allow";
|
||||
|
||||
"AuthConfirmation.LoginSuccess.Title" = "Login Successful";
|
||||
"AuthConfirmation.LoginSuccess.Text" = "You are now logged in to [%@]().";
|
||||
"AuthConfirmation.LoginSuccess.TextNoNumber" = "You are logged in to [%@](), but you didn't grant access to your phone number.";
|
||||
|
||||
"AuthConfirmation.LoginFail.Title" = "Login Failed";
|
||||
"AuthConfirmation.LoginFail.Text" = "Please try logging in to [%@]() again.";
|
||||
|
||||
"Notification.GroupCreatorChangePending" = "%2$@ will become the new main admin in 7 days if %1$@ does not return.";
|
||||
"Notification.GroupCreatorChangeApplied" = "%1$@ has transferred ownership of the group to %2$@.";
|
||||
|
|
|
|||
25
Telegram/Tests/Sources/UITests.swift
Normal file
25
Telegram/Tests/Sources/UITests.swift
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import XCTest
|
||||
|
||||
class UITests: XCTestCase {
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use recording to get started writing UI tests.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 4c51f10063687af77eabbbdc035124d798ca3a3f
|
||||
Subproject commit 7967e453d1f54963935a1ae06808e1c64420bfb1
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 5b448b48fc2fbd5456d55011811d77610dfd5069
|
||||
Subproject commit 39cc2529241335d20afcb6700c587d064e7a2870
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 5c45bcec63147fbdba1a164bdd36c2883fc3169f
|
||||
Subproject commit 2b9261134a123ad83f8b1d2dc7fc69ae83fe7172
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit df2419de2c8d44592dddf213124e6b48bd175768
|
||||
Subproject commit 79f26ec1315531a8459fe95934440cb3ca3c16df
|
||||
|
|
@ -316,7 +316,6 @@ public enum ResolvedUrl {
|
|||
case boost(peerId: PeerId?, status: ChannelBoostStatus?, myBoostStatus: MyBoostStatus?)
|
||||
case premiumGiftCode(slug: String)
|
||||
case premiumMultiGift(reference: String?)
|
||||
case collectible(gift: StarGift.UniqueGift?)
|
||||
case auction(auction: GiftAuctionContext?)
|
||||
case messageLink(link: TelegramResolvedMessageLink?)
|
||||
case stars
|
||||
|
|
@ -328,6 +327,13 @@ public enum ResolvedUrl {
|
|||
case unknownDeepLink(path: String)
|
||||
case oauth(url: String)
|
||||
|
||||
public enum ResolvedCollectible {
|
||||
case gift(StarGift.UniqueGift)
|
||||
case invalidSlug
|
||||
case alreadyBurned
|
||||
}
|
||||
case collectible(ResolvedCollectible)
|
||||
|
||||
public enum ChatsSection {
|
||||
case search
|
||||
case edit
|
||||
|
|
@ -732,6 +738,11 @@ public enum DeviceContactInfoSubject {
|
|||
}
|
||||
|
||||
public enum PeerInfoControllerMode {
|
||||
public enum PeerInfoMediaKind {
|
||||
case photoVideo
|
||||
case file
|
||||
}
|
||||
|
||||
case generic
|
||||
case calls(messages: [Message])
|
||||
case nearbyPeer(distance: Int32)
|
||||
|
|
@ -747,6 +758,7 @@ public enum PeerInfoControllerMode {
|
|||
case monoforum(EnginePeer.Id)
|
||||
case storyAlbum(id: Int64)
|
||||
case giftCollection(id: Int64)
|
||||
case media(kind: PeerInfoMediaKind, messageIndex: EngineMessage.Index)
|
||||
}
|
||||
|
||||
public enum ContactListActionItemInlineIconPosition {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ public final class OpenChatMessageParams {
|
|||
public let playlistLocation: PeerMessagesPlaylistLocation?
|
||||
public let gallerySource: GalleryControllerItemSource?
|
||||
public let centralItemUpdated: ((MessageId) -> Void)?
|
||||
public let navigateToMessageContext: ((EngineMessage) -> Void)?
|
||||
public let getSourceRect: (() -> CGRect?)?
|
||||
public let blockInteraction: Promise<Bool>
|
||||
|
||||
|
|
@ -81,6 +82,7 @@ public final class OpenChatMessageParams {
|
|||
playlistLocation: PeerMessagesPlaylistLocation? = nil,
|
||||
gallerySource: GalleryControllerItemSource? = nil,
|
||||
centralItemUpdated: ((MessageId) -> Void)? = nil,
|
||||
navigateToMessageContext: ((EngineMessage) -> Void)? = nil,
|
||||
getSourceRect: (() -> CGRect?)? = nil
|
||||
) {
|
||||
self.context = context
|
||||
|
|
@ -112,6 +114,7 @@ public final class OpenChatMessageParams {
|
|||
self.playlistLocation = playlistLocation
|
||||
self.gallerySource = gallerySource
|
||||
self.centralItemUpdated = centralItemUpdated
|
||||
self.navigateToMessageContext = navigateToMessageContext
|
||||
self.getSourceRect = getSourceRect
|
||||
self.blockInteraction = Promise()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,12 +114,14 @@ public extension ComponentTransition {
|
|||
options.insert(.allowUserInteraction)
|
||||
}
|
||||
switch curve {
|
||||
case .spring, .bounce:
|
||||
case .spring, .bounce, .custom:
|
||||
var parameters: CALayerSpringParametersOverrideParameters?
|
||||
var dampingValue: CGFloat = 500.0
|
||||
if case let .bounce(stiffness, damping) = curve {
|
||||
dampingValue = damping
|
||||
parameters = CALayerSpringParametersOverrideParameters(stiffness: stiffness, damping: damping, duration: duration)
|
||||
parameters = CALayerSpringParametersOverrideParametersSpring(stiffness: stiffness, damping: damping, duration: duration)
|
||||
} else if case let .custom(a, b, c, d) = curve {
|
||||
parameters = CALayerSpringParametersOverrideParametersCustomCurve(cp1: CGPoint(x: CGFloat(a), y: CGFloat(b)), cp2: CGPoint(x: CGFloat(c), y: CGFloat(d)))
|
||||
}
|
||||
CALayer.push(CALayerSpringParametersOverride(parameters: parameters))
|
||||
UIView.animate(withDuration: duration, delay: delay, usingSpringWithDamping: dampingValue, initialSpringVelocity: 0.0, options: options, animations: {
|
||||
|
|
|
|||
|
|
@ -1152,7 +1152,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
self.hasExpandedCaptionPromise.set(scrollView.contentOffset.y > 1.0)
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
self.validLayout = (size, metrics, leftInset, rightInset, bottomInset, contentInset)
|
||||
|
||||
let width = size.width
|
||||
|
|
@ -1177,6 +1177,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
buttonPanelInsets.right += 18.0
|
||||
}
|
||||
|
||||
var needsShadow = false
|
||||
if !self.textNode.isHidden {
|
||||
var textFrame = CGRect()
|
||||
var visibleTextHeight: CGFloat = 0.0
|
||||
|
|
@ -1191,6 +1192,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
var textOffset: CGFloat = 0.0
|
||||
var additionalTextHeight: CGFloat = 0.0
|
||||
if displayCaption {
|
||||
needsShadow = true
|
||||
|
||||
visibleTextHeight = textSize.height
|
||||
if visibleTextHeight > 100.0 {
|
||||
visibleTextHeight = 80.0
|
||||
|
|
@ -1272,8 +1275,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
|
||||
if let scrubberView = self.scrubberView, scrubberView.superview == self.view {
|
||||
panelHeight += 10.0
|
||||
if isLandscape, case .compact = metrics.widthClass {
|
||||
panelHeight += 14.0
|
||||
if isLandscape {
|
||||
panelHeight += 34.0
|
||||
} else {
|
||||
panelHeight += 34.0
|
||||
}
|
||||
|
|
@ -1409,6 +1412,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
}
|
||||
}
|
||||
|
||||
if !centerControlItems.isEmpty && rightControlItems.isEmpty {
|
||||
rightControlItems = centerControlItems
|
||||
centerControlItems = []
|
||||
}
|
||||
|
||||
let buttonPanelSize = self.buttonPanel.update(
|
||||
transition: ComponentTransition(transition),
|
||||
component: AnyComponent(GlassControlPanelComponent(
|
||||
|
|
@ -1482,7 +1490,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
|
||||
self.contentNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
|
||||
|
||||
return panelHeight
|
||||
return LayoutInfo(height: panelHeight, needsShadow: needsShadow)
|
||||
}
|
||||
|
||||
override func animateIn(transition: ContainedViewLayoutTransition) {
|
||||
|
|
@ -1500,7 +1508,8 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
scrubberView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
transition.animatePositionAdditive(node: self.scrollWrapperNode, offset: CGPoint(x: 0.0, y: self.bounds.height - fromHeight))
|
||||
self.scrollWrapperNode.alpha = 1.0
|
||||
self.scrollNode.alpha = 0.0
|
||||
ComponentTransition(transition).setAlpha(view: self.scrollNode.view, alpha: 1.0)
|
||||
if let buttonPanelView = self.buttonPanel.view {
|
||||
buttonPanelView.alpha = 1.0
|
||||
}
|
||||
|
|
@ -1510,7 +1519,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
self.statusNode.alpha = 1.0
|
||||
self.playbackControlButton.alpha = 1.0
|
||||
self.buttonNode?.alpha = 1.0
|
||||
self.scrollWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
|
||||
}
|
||||
|
||||
override func animateOut(transition: ContainedViewLayoutTransition) {
|
||||
|
|
@ -1527,7 +1535,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
scrubberView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
}
|
||||
transition.updateFrame(node: self.scrollWrapperNode, frame: self.scrollWrapperNode.frame.offsetBy(dx: 0.0, dy: self.bounds.height - toHeight))
|
||||
self.scrollWrapperNode.alpha = 0.0
|
||||
ComponentTransition(transition).setAlpha(view: self.scrollNode.view, alpha: 0.0, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
|
||||
if let buttonPanelView = self.buttonPanel.view {
|
||||
buttonPanelView.alpha = 0.0
|
||||
|
|
@ -1538,9 +1548,6 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll
|
|||
self.statusNode.alpha = 0.0
|
||||
self.playbackControlButton.alpha = 0.0
|
||||
self.buttonNode?.alpha = 0.0
|
||||
self.scrollWrapperNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
}
|
||||
|
||||
@objc func fullscreenButtonPressed() {
|
||||
|
|
|
|||
|
|
@ -587,6 +587,8 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
private let source: GalleryControllerItemSource
|
||||
private let invertItemOrder: Bool
|
||||
|
||||
private let titleView: GalleryTitleView
|
||||
|
||||
private let streamVideos: Bool
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
|
|
@ -617,7 +619,7 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
private var configuration: GalleryConfiguration?
|
||||
|
||||
private let centralItemTitle = Promise<String>()
|
||||
private let centralItemTitleView = Promise<UIView?>()
|
||||
private let centralItemTitleContent = Promise<GalleryTitleView.Content?>()
|
||||
private let centralItemRightBarButtonItem = Promise<UIBarButtonItem?>()
|
||||
private let centralItemRightBarButtonItems = Promise<[UIBarButtonItem]?>(nil)
|
||||
private let centralItemNavigationStyle = Promise<GalleryItemNodeNavigationStyle>()
|
||||
|
|
@ -645,6 +647,8 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
public var onDidAppear: (() -> Void)?
|
||||
public var useSimpleAnimation: Bool = false
|
||||
|
||||
public var navigateToMessageContext: ((EngineMessage) -> Void)?
|
||||
|
||||
private var initialOrientation: UIInterfaceOrientation?
|
||||
|
||||
public init(context: AccountContext, source: GalleryControllerItemSource, invertItemOrder: Bool = false, streamSingleVideo: Bool = false, fromPlayingVideo: Bool = false, landscape: Bool = false, timecode: Double? = nil, playbackRate: Double? = nil, synchronousLoad: Bool = false, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, actionInteraction: GalleryControllerActionInteraction? = nil, generateStoreAfterDownload: ((Message, TelegramMediaFile) -> (() -> Void)?)? = nil) {
|
||||
|
|
@ -680,6 +684,8 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
openActionOptionsImpl?(action, message)
|
||||
}
|
||||
|
||||
self.titleView = GalleryTitleView(context: context, presentationData: self.presentationData)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
|
||||
|
||||
let backItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Back, target: self, action: #selector(self.donePressed))
|
||||
|
|
@ -915,8 +921,8 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
self?.navigationItem.title = title
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(self.centralItemTitleView.get().start(next: { [weak self] titleView in
|
||||
self?.navigationItem.titleView = titleView
|
||||
self.centralItemAttributesDisposable.add(self.centralItemTitleContent.get().start(next: { [weak self] titleContent in
|
||||
self?.titleView.setContent(content: titleContent)
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(combineLatest(self.centralItemRightBarButtonItem.get(), self.centralItemRightBarButtonItems.get()).start(next: { [weak self] rightBarButtonItem, rightBarButtonItems in
|
||||
|
|
@ -933,7 +939,7 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
self.centralItemAttributesDisposable.add(self.centralItemFooterContentNode.get().start(next: { [weak self] footerContentNode, overlayContentNode in
|
||||
self?.galleryNode.updatePresentationState({
|
||||
$0.withUpdatedFooterContentNode(footerContentNode).withUpdatedOverlayContentNode(overlayContentNode)
|
||||
}, transition: .immediate)
|
||||
}, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(self.centralItemNavigationStyle.get().start(next: { [weak self] style in
|
||||
|
|
@ -1297,6 +1303,10 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
var animatedOutNode = true
|
||||
var animatedOutInterface = false
|
||||
|
||||
if forceAway {
|
||||
self._hiddenMedia.set(.single(nil))
|
||||
}
|
||||
|
||||
let completion = { [weak self] in
|
||||
if animatedOutNode && animatedOutInterface {
|
||||
self?.actionInteraction?.updateCanReadHistory(true)
|
||||
|
|
@ -1375,7 +1385,7 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
})
|
||||
|
||||
let disableTapNavigation = !(self.context.sharedContext.currentMediaDisplaySettings.with { $0 }.showNextMediaOnTap)
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, disableTapNavigation: disableTapNavigation)
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, titleView: titleView, disableTapNavigation: disableTapNavigation)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.galleryNode.statusBar = self.statusBar
|
||||
|
|
@ -1529,7 +1539,7 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
strongSelf.centralItemTitle.set(node.title())
|
||||
strongSelf.centralItemTitleView.set(node.titleView())
|
||||
strongSelf.centralItemTitleContent.set(node.titleContent())
|
||||
strongSelf.centralItemRightBarButtonItem.set(node.rightBarButtonItem())
|
||||
strongSelf.centralItemRightBarButtonItems.set(node.rightBarButtonItems())
|
||||
strongSelf.centralItemNavigationStyle.set(node.navigationStyle())
|
||||
|
|
@ -1715,7 +1725,7 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
let entry = self.entries[centralItemNode.index]
|
||||
|
||||
self.centralItemTitle.set(centralItemNode.title())
|
||||
self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
self.centralItemTitleContent.set(centralItemNode.titleContent())
|
||||
self.centralItemRightBarButtonItem.set(centralItemNode.rightBarButtonItem())
|
||||
self.centralItemRightBarButtonItems.set(centralItemNode.rightBarButtonItems())
|
||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||
|
|
@ -1768,7 +1778,7 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
if let centralItemNode = self.galleryNode.pager.centralItemNode() {
|
||||
let message = self.entries[centralItemNode.index].entry.message
|
||||
self.centralItemTitle.set(centralItemNode.title())
|
||||
self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
self.centralItemTitleContent.set(centralItemNode.titleContent())
|
||||
self.centralItemRightBarButtonItem.set(centralItemNode.rightBarButtonItem())
|
||||
self.centralItemRightBarButtonItems.set(centralItemNode.rightBarButtonItems())
|
||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||
|
|
@ -1913,4 +1923,11 @@ public class GalleryController: ViewController, StandalonePresentableController,
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func dismissAndNavigateToMessageContext(message: Message) {
|
||||
if let navigateToMessageContext = self.navigateToMessageContext {
|
||||
navigateToMessageContext(EngineMessage(message))
|
||||
}
|
||||
self.dismiss(forceAway: true)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,8 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
private let context: AccountContext
|
||||
|
||||
public var statusBar: StatusBar?
|
||||
public var navigationBar: NavigationBar? {
|
||||
didSet {
|
||||
|
||||
}
|
||||
}
|
||||
public var navigationBar: NavigationBar?
|
||||
private let titleView: GalleryTitleView?
|
||||
|
||||
private let headerEdgeEffectView: EdgeEffectView
|
||||
|
||||
|
|
@ -63,12 +60,13 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
}
|
||||
}
|
||||
|
||||
public init(context: AccountContext, controllerInteraction: GalleryControllerInteraction, pageGap: CGFloat = 20.0, disableTapNavigation: Bool = false) {
|
||||
public init(context: AccountContext, controllerInteraction: GalleryControllerInteraction, titleView: GalleryTitleView?, pageGap: CGFloat = 20.0, disableTapNavigation: Bool = false) {
|
||||
self.context = context
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.backgroundColor = UIColor.black
|
||||
self.scrollView = UIScrollView()
|
||||
self.scrollView.delaysContentTouches = false
|
||||
self.titleView = titleView
|
||||
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
|
|
@ -277,15 +275,19 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.pager.isInteractingUpdated = { [weak self] isInteracting in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.titleView?.updateIsInteracting(isInteracting: isInteracting)
|
||||
}
|
||||
}
|
||||
|
||||
override open func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *), !self.isLayerBacked {
|
||||
self.view.accessibilityIgnoresInvertColors = true
|
||||
}
|
||||
|
||||
self.view.accessibilityIgnoresInvertColors = true
|
||||
self.view.disablesInteractiveTransitionGestureRecognizer = true
|
||||
}
|
||||
|
||||
|
|
@ -312,9 +314,24 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
if self.footerNode.supernode == nil {
|
||||
self.addSubnode(self.footerNode)
|
||||
}
|
||||
if self.headerEdgeEffectView.superview == nil {
|
||||
self.view.insertSubview(self.headerEdgeEffectView, belowSubview: navigationBar.view)
|
||||
if let titleView = self.titleView {
|
||||
if titleView.superview == nil {
|
||||
self.view.addSubview(titleView)
|
||||
}
|
||||
|
||||
let titleHeight: CGFloat = navigationBarHeight - (layout.statusBarHeight ?? 0.0) - 3.0
|
||||
let titleSize = CGSize(width: layout.size.width - (layout.safeInsets.left + 44.0 + 16.0 + 8.0) * 2.0, height: titleHeight)
|
||||
var titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - titleSize.width) * 0.5), y: (layout.statusBarHeight ?? 0.0) + 4.0), size: titleSize)
|
||||
if self.areControlsHidden {
|
||||
titleFrame.origin.y -= navigationBarHeight
|
||||
}
|
||||
let _ = titleView.updateLayout(availableSize: titleFrame.size, transition: .immediate)
|
||||
transition.updateFrame(view: titleView, frame: titleFrame)
|
||||
}
|
||||
|
||||
/*if self.headerEdgeEffectView.superview == nil {
|
||||
self.view.insertSubview(self.headerEdgeEffectView, belowSubview: navigationBar.view)
|
||||
}*/
|
||||
}
|
||||
|
||||
var thumbnailPanelHeight: CGFloat = 0.0
|
||||
|
|
@ -370,7 +387,7 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
self.statusBar?.updateAlpha(alpha, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
})
|
||||
self.footerNode.setVisibilityAlpha(alpha, animated: animated)
|
||||
self.updateThumbnailContainerNodeAlpha(.immediate)
|
||||
self.updateThumbnailContainerNodeAlpha(animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate)
|
||||
|
||||
if let (navigationBarHeight, layout) = self.containerLayout {
|
||||
self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .animated(duration: 0.4, curve: .spring))
|
||||
|
|
@ -404,6 +421,7 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
self.navigationBar?.alpha = 0.0
|
||||
self.footerNode.alpha = 0.0
|
||||
self.headerEdgeEffectView.alpha = 0.0
|
||||
self.titleView?.alpha = 0.0
|
||||
self.currentThumbnailContainerNode?.alpha = 0.0
|
||||
|
||||
self.backgroundNode.layer.animate(from: backgroundColor.withAlphaComponent(0.0).cgColor, to: backgroundColor.cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.15)
|
||||
|
|
@ -420,7 +438,10 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
self.footerNode.alpha = 1.0
|
||||
self.footerNode.animateIn(transition: .animated(duration: 0.15, curve: .linear))
|
||||
|
||||
ComponentTransition.easeInOut(duration: 0.15).setAlpha(view: self.headerEdgeEffectView, alpha: 1.0)
|
||||
ComponentTransition.easeInOut(duration: 0.15).animateView {
|
||||
self.headerEdgeEffectView.alpha = 1.0
|
||||
self.titleView?.alpha = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
if animateContent {
|
||||
|
|
@ -461,7 +482,10 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
interfaceAnimationCompleted = true
|
||||
intermediateCompletion()
|
||||
})
|
||||
ComponentTransition.easeInOut(duration: 0.1).setAlpha(view: self.headerEdgeEffectView, alpha: 0.0)
|
||||
ComponentTransition.easeInOut(duration: 0.1).animateView {
|
||||
self.headerEdgeEffectView.alpha = 0.0
|
||||
self.titleView?.alpha = 0.0
|
||||
}
|
||||
|
||||
self.footerNode.animateOut(transition: .animated(duration: 0.1, curve: .easeInOut))
|
||||
|
||||
|
|
@ -522,6 +546,8 @@ open class GalleryControllerNode: ASDisplayNode, ASScrollViewDelegate, ASGesture
|
|||
if let overlayNode = self.overlayNode {
|
||||
overlayNode.alpha = transition
|
||||
}
|
||||
self.headerEdgeEffectView.alpha = transition
|
||||
self.titleView?.alpha = transition
|
||||
}
|
||||
|
||||
open func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ public final class GalleryControllerInteraction {
|
|||
}
|
||||
|
||||
open class GalleryFooterContentNode: ASDisplayNode {
|
||||
public struct LayoutInfo {
|
||||
let height: CGFloat
|
||||
let needsShadow: Bool
|
||||
|
||||
public init(height: CGFloat, needsShadow: Bool) {
|
||||
self.height = height
|
||||
self.needsShadow = needsShadow
|
||||
}
|
||||
}
|
||||
|
||||
public var requestLayout: ((ContainedViewLayoutTransition) -> Void)?
|
||||
public var controllerInteraction: GalleryControllerInteraction?
|
||||
|
||||
|
|
@ -37,8 +47,8 @@ open class GalleryFooterContentNode: ASDisplayNode {
|
|||
self.alpha = alpha
|
||||
}
|
||||
|
||||
open func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
return 0.0
|
||||
open func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
return LayoutInfo(height: 0.0, needsShadow: false)
|
||||
}
|
||||
|
||||
open func animateIn(transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -29,10 +29,14 @@ public final class GalleryFooterNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
private var visibilityAlpha: CGFloat = 1.0
|
||||
private var isEdgeEffectVisible: Bool = false
|
||||
|
||||
public func setVisibilityAlpha(_ alpha: CGFloat, animated: Bool) {
|
||||
self.visibilityAlpha = alpha
|
||||
let transition: ComponentTransition = animated ? .easeInOut(duration: 0.2) : .immediate
|
||||
transition.setAlpha(view: self.edgeEffectView, alpha: alpha)
|
||||
if self.isEdgeEffectVisible {
|
||||
transition.setAlpha(view: self.edgeEffectView, alpha: alpha)
|
||||
}
|
||||
self.currentFooterContentNode?.setVisibilityAlpha(alpha, animated: true)
|
||||
self.currentOverlayContentNode?.setVisibilityAlpha(alpha)
|
||||
}
|
||||
|
|
@ -107,32 +111,38 @@ public final class GalleryFooterNode: ASDisplayNode {
|
|||
if layout.size.width > layout.size.height {
|
||||
effectiveThumbnailPanelHeight = 0.0
|
||||
}
|
||||
var backgroundHeight: CGFloat = 0.0
|
||||
var backgroundLayoutInfo: GalleryFooterContentNode.LayoutInfo?
|
||||
let verticalOffset: CGFloat = isHidden ? (layout.size.width > layout.size.height ? 44.0 : (effectiveThumbnailPanelHeight > 0.0 ? 106.0 : 54.0)) : 0.0
|
||||
let backgroundFrame: CGRect
|
||||
var edgeEffectTransition = ComponentTransition(transition)
|
||||
if let footerContentNode = self.currentFooterContentNode {
|
||||
backgroundHeight = footerContentNode.updateLayout(size: layout.size, metrics: layout.metrics, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, contentInset: effectiveThumbnailPanelHeight, transition: transition)
|
||||
transition.updateFrame(node: footerContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundHeight + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundHeight)))
|
||||
var footerTransition = transition
|
||||
if footerContentNode.bounds.isEmpty {
|
||||
footerTransition = .immediate
|
||||
}
|
||||
let backgroundLayoutInfoValue = footerContentNode.updateLayout(size: layout.size, metrics: layout.metrics, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, bottomInset: cleanInsets.bottom, contentInset: effectiveThumbnailPanelHeight, transition: footerTransition)
|
||||
backgroundLayoutInfo = backgroundLayoutInfoValue
|
||||
|
||||
footerTransition.updateFrame(node: footerContentNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundLayoutInfoValue.height + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundLayoutInfoValue.height)))
|
||||
if let dismissedCurrentFooterContentNode = dismissedCurrentFooterContentNode {
|
||||
let contentTransition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||
footerContentNode.animateIn(fromHeight: dismissedCurrentFooterContentNode.bounds.height, previousContentNode: dismissedCurrentFooterContentNode, transition: contentTransition)
|
||||
dismissedCurrentFooterContentNode.animateOut(toHeight: backgroundHeight, nextContentNode: footerContentNode, transition: contentTransition, completion: { [weak self, weak dismissedCurrentFooterContentNode] in
|
||||
dismissedCurrentFooterContentNode.animateOut(toHeight: backgroundLayoutInfoValue.height, nextContentNode: footerContentNode, transition: contentTransition, completion: { [weak self, weak dismissedCurrentFooterContentNode] in
|
||||
if let strongSelf = self, let dismissedCurrentFooterContentNode = dismissedCurrentFooterContentNode, dismissedCurrentFooterContentNode !== strongSelf.currentFooterContentNode {
|
||||
dismissedCurrentFooterContentNode.removeFromSupernode()
|
||||
}
|
||||
})
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundHeight + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundHeight))
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundLayoutInfoValue.height + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundLayoutInfoValue.height))
|
||||
edgeEffectTransition = ComponentTransition(contentTransition)
|
||||
} else {
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundHeight + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundHeight))
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundLayoutInfoValue.height + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundLayoutInfoValue.height))
|
||||
}
|
||||
} else {
|
||||
if let dismissedCurrentFooterContentNode = dismissedCurrentFooterContentNode {
|
||||
dismissedCurrentFooterContentNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - backgroundHeight + verticalOffset), size: CGSize(width: layout.size.width, height: backgroundHeight))
|
||||
backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height + verticalOffset), size: CGSize(width: layout.size.width, height: 0.0))
|
||||
}
|
||||
|
||||
self.contentsFrame = backgroundFrame
|
||||
|
|
@ -144,9 +154,20 @@ public final class GalleryFooterNode: ASDisplayNode {
|
|||
edgeEffectFrame.size.height += edgeEffectOffset
|
||||
edgeEffectTransition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: .black, alpha: 0.65, rect: edgeEffectFrame, edge: .bottom, edgeSize: min(edgeEffectHeight, edgeEffectFrame.height), transition: edgeEffectTransition)
|
||||
if let backgroundLayoutInfo, backgroundLayoutInfo.needsShadow {
|
||||
self.isEdgeEffectVisible = true
|
||||
ComponentTransition(transition).setAlpha(view: self.edgeEffectView, alpha: self.visibilityAlpha)
|
||||
} else {
|
||||
self.isEdgeEffectVisible = false
|
||||
ComponentTransition(transition).setAlpha(view: self.edgeEffectView, alpha: 0.0)
|
||||
}
|
||||
|
||||
let contentTransition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
|
||||
if let overlayContentNode = self.currentOverlayContentNode {
|
||||
var backgroundHeight: CGFloat = 0.0
|
||||
if let backgroundLayoutInfo {
|
||||
backgroundHeight = backgroundLayoutInfo.height
|
||||
}
|
||||
let insets = UIEdgeInsets(top: navigationBarHeight, left: layout.safeInsets.left, bottom: isHidden ? layout.intrinsicInsets.bottom : backgroundHeight, right: layout.safeInsets.right)
|
||||
overlayContentNode.updateLayout(size: layout.size, metrics: layout.metrics, insets: insets, isHidden: isHidden, transition: transition)
|
||||
transition.updateFrame(node: overlayContentNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ open class GalleryItemNode: ASDisplayNode {
|
|||
return .single("")
|
||||
}
|
||||
|
||||
open func titleView() -> Signal<UIView?, NoError> {
|
||||
open func titleContent() -> Signal<GalleryTitleView.Content?, NoError> {
|
||||
return .single(nil)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ public final class GalleryPagerNode: ASDisplayNode, ASScrollViewDelegate, ASGest
|
|||
public var completeCustomDismiss: (Bool) -> Void = { _ in }
|
||||
public var baseNavigationController: () -> NavigationController? = { return nil }
|
||||
public var galleryController: () -> ViewController? = { return nil }
|
||||
public var isInteractingUpdated: ((Bool) -> Void)?
|
||||
|
||||
private var pagingEnabled = true
|
||||
public var pagingEnabledPromise = Promise<Bool>(true)
|
||||
|
|
@ -771,15 +772,18 @@ public final class GalleryPagerNode: ASDisplayNode, ASScrollViewDelegate, ASGest
|
|||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
if !decelerate {
|
||||
self.ensureItemsLoaded(force: false)
|
||||
self.isInteractingUpdated?(false)
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
self.ensureItemsLoaded(force: true)
|
||||
self.isInteractingUpdated?(true)
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||
self.ensureItemsLoaded(force: true)
|
||||
self.isInteractingUpdated?(false)
|
||||
}
|
||||
|
||||
private func shouldLoadItems(force: Bool) -> Bool {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import AsyncDisplayKit
|
|||
import Display
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
import GlassBackgroundComponent
|
||||
|
|
@ -14,7 +15,37 @@ import MultilineTextComponent
|
|||
private let titleFont = Font.semibold(17.0)
|
||||
private let dateFont = Font.regular(12.0)
|
||||
|
||||
final class GalleryTitleView: UIView, NavigationBarTitleView {
|
||||
public final class GalleryTitleView: UIView, NavigationBarTitleView {
|
||||
public final class Content: Equatable {
|
||||
let message: EngineMessage
|
||||
let title: String?
|
||||
let action: (() -> Void)?
|
||||
|
||||
public init(message: EngineMessage, title: String?, action: (() -> Void)?) {
|
||||
self.message = message
|
||||
self.title = title
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public static func ==(lhs: Content, rhs: Content) -> Bool {
|
||||
if lhs.message != rhs.message {
|
||||
return false
|
||||
}
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if (lhs.action == nil) != (rhs.action == nil) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
|
||||
private let backgroundContainer: GlassBackgroundContainerView
|
||||
private let backgroundView: GlassBackgroundView
|
||||
private let authorNameNode: ASTextNode
|
||||
private let dateNode: ASTextNode
|
||||
|
||||
|
|
@ -22,16 +53,28 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
|
|||
private let titleBackground: GlassBackgroundView
|
||||
private let title = ComponentView<Empty>()
|
||||
|
||||
private var content: Content?
|
||||
private var titleString: String?
|
||||
|
||||
var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
|
||||
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
private var interactionTimer: Foundation.Timer?
|
||||
|
||||
public init(context: AccountContext, presentationData: PresentationData) {
|
||||
self.context = context
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundContainer = GlassBackgroundContainerView()
|
||||
self.backgroundView = GlassBackgroundView()
|
||||
self.backgroundContainer.contentView.addSubview(self.backgroundView)
|
||||
|
||||
self.authorNameNode = ASTextNode()
|
||||
self.authorNameNode.isUserInteractionEnabled = false
|
||||
self.authorNameNode.displaysAsynchronously = false
|
||||
self.authorNameNode.maximumNumberOfLines = 1
|
||||
|
||||
self.dateNode = ASTextNode()
|
||||
self.dateNode.isUserInteractionEnabled = false
|
||||
self.dateNode.displaysAsynchronously = false
|
||||
self.dateNode.maximumNumberOfLines = 1
|
||||
|
||||
|
|
@ -40,45 +83,90 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
|
|||
|
||||
self.titleBackgroundContainer.contentView.addSubview(self.titleBackground)
|
||||
|
||||
super.init(frame: frame)
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.addSubnode(self.authorNameNode)
|
||||
self.addSubnode(self.dateNode)
|
||||
self.addSubview(self.backgroundContainer)
|
||||
|
||||
self.backgroundView.contentView.addSubview(self.authorNameNode.view)
|
||||
self.backgroundView.contentView.addSubview(self.dateNode.view)
|
||||
|
||||
self.backgroundContainer.isHidden = true
|
||||
|
||||
self.addSubview(self.titleBackgroundContainer)
|
||||
self.titleBackgroundContainer.alpha = 0.0
|
||||
|
||||
self.backgroundView.contentView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTapGesture(_:))))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setMessage(_ message: Message, presentationData: PresentationData, accountPeerId: PeerId, title: String?) {
|
||||
let authorNameText = stringForFullAuthorName(message: EngineMessage(message), strings: presentationData.strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: accountPeerId).first ?? ""
|
||||
let dateText = humanReadableStringForTimestamp(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, timestamp: message.timestamp).string
|
||||
|
||||
self.authorNameNode.attributedText = NSAttributedString(string: authorNameText, font: titleFont, textColor: .white)
|
||||
self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: UIColor(white: 1.0, alpha: 0.5))
|
||||
|
||||
self.titleString = title
|
||||
deinit {
|
||||
self.interactionTimer?.invalidate()
|
||||
}
|
||||
|
||||
func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
@objc private func onTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.content?.action?()
|
||||
}
|
||||
}
|
||||
|
||||
public func setContent(content: Content?) {
|
||||
self.content = content
|
||||
|
||||
self.backgroundContainer.isHidden = self.content == nil
|
||||
|
||||
if let content {
|
||||
let authorNameText = stringForFullAuthorName(message: content.message, strings: self.presentationData.strings, nameDisplayOrder: self.presentationData.nameDisplayOrder, accountPeerId: self.context.account.peerId).first ?? ""
|
||||
let dateText = humanReadableStringForTimestamp(strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, timestamp: content.message.timestamp).string
|
||||
|
||||
self.authorNameNode.attributedText = NSAttributedString(string: authorNameText, font: titleFont, textColor: .white)
|
||||
self.dateNode.attributedText = NSAttributedString(string: dateText, font: dateFont, textColor: UIColor(white: 1.0, alpha: 0.5))
|
||||
}
|
||||
|
||||
self.titleString = content?.title
|
||||
|
||||
if !self.bounds.isEmpty {
|
||||
let _ = self.updateLayout(availableSize: self.bounds.size, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
let size = availableSize
|
||||
|
||||
let leftInset: CGFloat = 0.0
|
||||
let rightInset: CGFloat = 0.0
|
||||
let leftInset: CGFloat = 8.0 + 14.0
|
||||
let rightInset: CGFloat = 8.0 + 14.0
|
||||
|
||||
let authorNameSize = self.authorNameNode.measure(CGSize(width: max(1.0, size.width - 8.0 * 2.0 - leftInset - rightInset), height: CGFloat.greatestFiniteMagnitude))
|
||||
let dateSize = self.dateNode.measure(CGSize(width: max(1.0, size.width - 8.0 * 2.0), height: CGFloat.greatestFiniteMagnitude))
|
||||
|
||||
var backgroundSize = CGSize(width: 0.0, height: 44.0)
|
||||
|
||||
if authorNameSize.height.isZero {
|
||||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height) / 2.0)), size: dateSize)
|
||||
backgroundSize.width = dateSize.width
|
||||
} else {
|
||||
backgroundSize.width = max(authorNameSize.width, dateSize.width)
|
||||
}
|
||||
backgroundSize.width = max(150.0, backgroundSize.width)
|
||||
backgroundSize.width += 14.0 * 2.0
|
||||
|
||||
if authorNameSize.height.isZero {
|
||||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - dateSize.width) / 2.0), y: floor((backgroundSize.height - dateSize.height) / 2.0)), size: dateSize)
|
||||
} else {
|
||||
let labelsSpacing: CGFloat = 2.0
|
||||
self.authorNameNode.frame = CGRect(origin: CGPoint(x: floor((size.width - authorNameSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0)), size: authorNameSize)
|
||||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
|
||||
self.authorNameNode.frame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - authorNameSize.width) / 2.0), y: floor((backgroundSize.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0)), size: authorNameSize)
|
||||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((backgroundSize.width - dateSize.width) / 2.0), y: floor((backgroundSize.height - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
|
||||
}
|
||||
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: floor((size.width - backgroundSize.width) * 0.5), y: floor((size.height - backgroundSize.height) * 0.5)), size: backgroundSize)
|
||||
|
||||
self.backgroundContainer.update(size: backgroundFrame.size, isDark: true, transition: ComponentTransition(transition))
|
||||
ComponentTransition(transition).setFrame(view: self.backgroundContainer, frame: backgroundFrame)
|
||||
|
||||
ComponentTransition(transition).setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: true, tintColor: .init(kind: .panel), isInteractive: self.content?.action != nil, transition: ComponentTransition(transition))
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
|
|
@ -112,7 +200,27 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
|
|||
return availableSize
|
||||
}
|
||||
|
||||
func animateLayoutTransition() {
|
||||
|
||||
public func animateLayoutTransition() {
|
||||
}
|
||||
|
||||
public func updateIsInteracting(isInteracting: Bool) {
|
||||
if isInteracting {
|
||||
self.interactionTimer?.invalidate()
|
||||
self.interactionTimer = nil
|
||||
|
||||
ComponentTransition.easeInOut(duration: 0.2).animateView {
|
||||
self.titleBackgroundContainer.alpha = 1.0
|
||||
}
|
||||
} else {
|
||||
self.interactionTimer?.invalidate()
|
||||
self.interactionTimer = Foundation.Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false, block: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
ComponentTransition.easeInOut(duration: 0.2).animateView {
|
||||
self.titleBackgroundContainer.alpha = 0.0
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ class ChatImageGalleryItem: GalleryItem {
|
|||
func node(synchronous: Bool) -> GalleryItemNode {
|
||||
let node = ChatImageGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present)
|
||||
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected, isSecret: self.isSecret)
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected, isSecret: self.isSecret, location: self.location)
|
||||
for media in self.message.media {
|
||||
if let paidContent = media as? TelegramMediaPaidContent {
|
||||
let mediaIndex = self.mediaIndex ?? 0
|
||||
|
|
@ -175,26 +175,12 @@ class ChatImageGalleryItem: GalleryItem {
|
|||
}
|
||||
}
|
||||
|
||||
var title: String?
|
||||
if let _ = message.adAttribute {
|
||||
title = self.presentationData.strings.Gallery_Ad
|
||||
} else if let location = self.location {
|
||||
title = self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").string
|
||||
}
|
||||
|
||||
node.titleContentView?.setMessage(self.message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId, title: title)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func updateNode(node: GalleryItemNode, synchronous: Bool) {
|
||||
if let node = node as? ChatImageGalleryItemNode, let location = self.location {
|
||||
let title = self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").string
|
||||
|
||||
if self.displayInfoOnTop {
|
||||
node.titleContentView?.setMessage(self.message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId, title: title)
|
||||
}
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected, isSecret: self.isSecret)
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop, translateToLanguage: self.translateToLanguage, peerIsCopyProtected: self.peerIsCopyProtected, isSecret: self.isSecret, location: location)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -251,12 +237,11 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
private var tilingNode: TilingNode?
|
||||
fileprivate let _ready = Promise<Void>()
|
||||
fileprivate let _title = Promise<String>()
|
||||
fileprivate let _titleView = Promise<UIView?>()
|
||||
fileprivate let _titleContent = Promise<GalleryTitleView.Content?>(nil)
|
||||
fileprivate let _rightBarButtonItems = Promise<[UIBarButtonItem]?>(nil)
|
||||
private let statusNodeContainer: HighlightableButtonNode
|
||||
private let statusNode: RadialStatusNode
|
||||
private let footerContentNode: ChatItemGalleryFooterContentNode
|
||||
fileprivate var titleContentView: GalleryTitleView?
|
||||
|
||||
private var contextAndMedia: (AccountContext, AnyMediaReference)?
|
||||
|
||||
|
|
@ -320,9 +305,6 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
|
||||
self.statusNodeContainer.isUserInteractionEnabled = false
|
||||
|
||||
self.titleContentView = GalleryTitleView(frame: CGRect())
|
||||
self._titleView.set(.single(self.titleContentView))
|
||||
|
||||
self.recognitionOverlayContentNode.action = { [weak self] active in
|
||||
if let strongSelf = self {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
|
||||
|
|
@ -459,7 +441,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
}))
|
||||
}
|
||||
|
||||
fileprivate func setMessage(_ message: Message, displayInfo: Bool, translateToLanguage: String?, peerIsCopyProtected: Bool, isSecret: Bool) {
|
||||
fileprivate func setMessage(_ message: Message, displayInfo: Bool, translateToLanguage: String?, peerIsCopyProtected: Bool, isSecret: Bool, location: MessageHistoryEntryLocation?) {
|
||||
self.message = message
|
||||
self.displayInfo = displayInfo
|
||||
self.translateToLanguage = translateToLanguage
|
||||
|
|
@ -467,6 +449,23 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
self.isSecret = isSecret
|
||||
self.imageNode.captureProtected = message.id.peerId.namespace == Namespaces.Peer.SecretChat || message.isCopyProtected() || peerIsCopyProtected || isSecret || message.paidContent != nil
|
||||
self.updateFooter(animated: false)
|
||||
|
||||
var title: String?
|
||||
if let _ = message.adAttribute {
|
||||
title = self.presentationData.strings.Gallery_Ad
|
||||
} else if let location {
|
||||
title = self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").string
|
||||
}
|
||||
|
||||
self._titleContent.set(.single(GalleryTitleView.Content(message: EngineMessage(message), title: title, action: message.adAttribute == nil ? { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let controller = self.galleryController() as? GalleryController else {
|
||||
return
|
||||
}
|
||||
controller.dismissAndNavigateToMessageContext(message: message)
|
||||
} : nil)))
|
||||
}
|
||||
|
||||
private func updateFooter(animated: Bool) {
|
||||
|
|
@ -1133,8 +1132,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
return self._title.get()
|
||||
}
|
||||
|
||||
override func titleView() -> Signal<UIView?, NoError> {
|
||||
return self._titleView.get()
|
||||
override func titleContent() -> Signal<GalleryTitleView.Content?, NoError> {
|
||||
return self._titleContent.get()
|
||||
}
|
||||
|
||||
override func rightBarButtonItems() -> Signal<[UIBarButtonItem]?, NoError> {
|
||||
|
|
|
|||
|
|
@ -101,34 +101,14 @@ public class UniversalVideoGalleryItem: GalleryItem {
|
|||
public func node(synchronous: Bool) -> GalleryItemNode {
|
||||
let node = UniversalVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present)
|
||||
|
||||
var title: String?
|
||||
if let indexData = self.indexData {
|
||||
title = self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").string
|
||||
} else if case let .message(message, _) = self.contentInfo, let _ = message.adAttribute {
|
||||
title = self.presentationData.strings.Gallery_Ad
|
||||
}
|
||||
|
||||
node.setupItem(self)
|
||||
|
||||
if case let .message(message, _) = self.contentInfo {
|
||||
node.titleContentView?.setMessage(message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId, title: title)
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
public func updateNode(node: GalleryItemNode, synchronous: Bool) {
|
||||
if let node = node as? UniversalVideoGalleryItemNode {
|
||||
var title: String?
|
||||
if let indexData = self.indexData {
|
||||
title = self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").string
|
||||
}
|
||||
|
||||
node.setupItem(self)
|
||||
|
||||
if self.displayInfoOnTop, case let .message(message, _) = self.contentInfo {
|
||||
node.titleContentView?.setMessage(message, presentationData: self.presentationData, accountPeerId: self.context.account.peerId, title: title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -881,7 +861,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
|
||||
fileprivate let _ready = Promise<Void>()
|
||||
fileprivate let _title = Promise<String>()
|
||||
fileprivate let _titleView = Promise<UIView?>()
|
||||
fileprivate let _titleContent = Promise<GalleryTitleView.Content?>(nil)
|
||||
fileprivate let _rightBarButtonItems = Promise<[UIBarButtonItem]?>()
|
||||
|
||||
fileprivate var titleContentView: GalleryTitleView?
|
||||
|
|
@ -1136,9 +1116,6 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
self.openMoreMenu(sourceView: self.moreBarButton.referenceNode.view, gesture: gesture, adMessage: adMessage, isSettings: false)
|
||||
}
|
||||
|
||||
self.titleContentView = GalleryTitleView(frame: CGRect())
|
||||
self._titleView.set(.single(self.titleContentView))
|
||||
|
||||
let shouldHideControlsSignal: Signal<Void, NoError> = combineLatest(self.isPlayingPromise.get(), self.isInteractingPromise.get(), self.controlsVisiblePromise.get(), self.isShowingContextMenuPromise.get(), self.isShowingSettingsMenuPromise.get(), self.isShowingAdMenuPromise.get(), self.hasExpandedCaptionPromise.get())
|
||||
|> mapToSignal { isPlaying, isInteracting, controlsVisible, isShowingContextMenu, isShowingSettingsMenu, isShowingAdMenu, hasExpandedCaptionPromise -> Signal<Void, NoError> in
|
||||
if isShowingContextMenu || isShowingSettingsMenu || isShowingAdMenu || hasExpandedCaptionPromise {
|
||||
|
|
@ -1962,6 +1939,25 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
}
|
||||
self.overlayContentNode.setMessage(context: item.context, message: message)
|
||||
}
|
||||
|
||||
var title: String?
|
||||
if let indexData = item.indexData {
|
||||
title = self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").string
|
||||
} else if case let .message(message, _) = item.contentInfo, let _ = message.adAttribute {
|
||||
title = self.presentationData.strings.Gallery_Ad
|
||||
}
|
||||
|
||||
if case let .message(message, _) = item.contentInfo {
|
||||
self._titleContent.set(.single(GalleryTitleView.Content(message: EngineMessage(message), title: title, action: message.adAttribute == nil ? { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
guard let controller = self.galleryController() as? GalleryController else {
|
||||
return
|
||||
}
|
||||
controller.dismissAndNavigateToMessageContext(message: message)
|
||||
} : nil)))
|
||||
}
|
||||
}
|
||||
|
||||
override func controlsVisibilityUpdated(isVisible: Bool) {
|
||||
|
|
@ -2759,8 +2755,8 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||
return self._title.get()
|
||||
}
|
||||
|
||||
override func titleView() -> Signal<UIView?, NoError> {
|
||||
return self._titleView.get()
|
||||
override func titleContent() -> Signal<GalleryTitleView.Content?, NoError> {
|
||||
return self._titleContent.get()
|
||||
}
|
||||
|
||||
override func rightBarButtonItems() -> Signal<[UIBarButtonItem]?, NoError> {
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ public final class SecretMediaPreviewController: ViewController {
|
|||
}, currentItemNode: { [weak self] in
|
||||
return self?.controllerNode.pager.centralItemNode()
|
||||
})
|
||||
self.displayNode = SecretMediaPreviewControllerNode(context: self.context, controllerInteraction: controllerInteraction)
|
||||
self.displayNode = SecretMediaPreviewControllerNode(context: self.context, controllerInteraction: controllerInteraction, titleView: nil)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.controllerNode.statusPressed = { [weak self] _ in
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ final class SecretMediaPreviewFooterContentNode: GalleryFooterContentNode {
|
|||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
let width = size.width
|
||||
let panelHeight: CGFloat = 44.0 + bottomInset
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ final class SecretMediaPreviewFooterContentNode: GalleryFooterContentNode {
|
|||
let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude))
|
||||
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floor((width - textSize.width) / 2.0), y: floor((44.0 - textSize.height) / 2.0)), size: textSize))
|
||||
|
||||
return panelHeight
|
||||
return LayoutInfo(height: panelHeight, needsShadow: false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -379,7 +379,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
|
|||
}, currentItemNode: { [weak self] in
|
||||
return self?.galleryNode.pager.centralItemNode()
|
||||
})
|
||||
self.displayNode = GalleryControllerNode(context: self.context,controllerInteraction: controllerInteraction)
|
||||
self.displayNode = GalleryControllerNode(context: self.context,controllerInteraction: controllerInteraction, titleView: nil)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.galleryNode.statusBar = self.statusBar
|
||||
|
|
@ -417,7 +417,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
|
|||
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
strongSelf.centralItemTitle.set(node.title())
|
||||
strongSelf.centralItemTitleView.set(node.titleView())
|
||||
//strongSelf.centralItemTitleView.set(node.titleView())
|
||||
strongSelf.centralItemRightBarButtonItem.set(node.rightBarButtonItem())
|
||||
strongSelf.centralItemRightBarButtonItems.set(node.rightBarButtonItems())
|
||||
strongSelf.centralItemNavigationStyle.set(node.navigationStyle())
|
||||
|
|
@ -448,7 +448,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable
|
|||
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let presentationArguments = self.presentationArguments as? InstantPageGalleryControllerPresentationArguments {
|
||||
self.centralItemTitle.set(centralItemNode.title())
|
||||
self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
//self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
self.centralItemRightBarButtonItem.set(centralItemNode.rightBarButtonItem())
|
||||
self.centralItemRightBarButtonItems.set(centralItemNode.rightBarButtonItems())
|
||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ final class InstantPageGalleryFooterContentNode: GalleryFooterContentNode {
|
|||
self.actionButton.isHidden = shareMedia == nil
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
let width = size.width
|
||||
var panelHeight: CGFloat = 44.0 + bottomInset + contentInset
|
||||
if !self.textNode.isHidden {
|
||||
|
|
@ -126,7 +126,7 @@ final class InstantPageGalleryFooterContentNode: GalleryFooterContentNode {
|
|||
|
||||
self.actionButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0))
|
||||
|
||||
return panelHeight
|
||||
return LayoutInfo(height: panelHeight, needsShadow: false)
|
||||
}
|
||||
|
||||
override func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -477,6 +477,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||
let header: ListViewItemHeader?
|
||||
let shimmering: ItemListPeerItemShimmering?
|
||||
let displayDecorations: Bool
|
||||
let displayBackground: Bool
|
||||
let disableInteractiveTransitionIfNecessary: Bool
|
||||
let storyStats: PeerStoryStats?
|
||||
let openStories: ((UIView) -> Void)?
|
||||
|
|
@ -520,6 +521,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||
header: ListViewItemHeader? = nil,
|
||||
shimmering: ItemListPeerItemShimmering? = nil,
|
||||
displayDecorations: Bool = true,
|
||||
displayBackground: Bool = true,
|
||||
disableInteractiveTransitionIfNecessary: Bool = false,
|
||||
storyStats: PeerStoryStats? = nil,
|
||||
openStories: ((UIView) -> Void)? = nil
|
||||
|
|
@ -562,6 +564,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||
self.header = header
|
||||
self.shimmering = shimmering
|
||||
self.displayDecorations = displayDecorations
|
||||
self.displayBackground = displayBackground
|
||||
self.disableInteractiveTransitionIfNecessary = disableInteractiveTransitionIfNecessary
|
||||
self.storyStats = storyStats
|
||||
self.openStories = openStories
|
||||
|
|
@ -606,6 +609,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||
header: ListViewItemHeader? = nil,
|
||||
shimmering: ItemListPeerItemShimmering? = nil,
|
||||
displayDecorations: Bool = true,
|
||||
displayBackground: Bool = true,
|
||||
disableInteractiveTransitionIfNecessary: Bool = false,
|
||||
storyStats: PeerStoryStats? = nil,
|
||||
openStories: ((UIView) -> Void)? = nil
|
||||
|
|
@ -648,6 +652,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem {
|
|||
self.header = header
|
||||
self.shimmering = shimmering
|
||||
self.displayDecorations = displayDecorations
|
||||
self.displayBackground = displayBackground
|
||||
self.disableInteractiveTransitionIfNecessary = disableInteractiveTransitionIfNecessary
|
||||
self.storyStats = storyStats
|
||||
self.openStories = openStories
|
||||
|
|
@ -1337,6 +1342,8 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
|
|||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||
|
||||
strongSelf.backgroundNode.isHidden = !item.displayBackground
|
||||
|
||||
let revealOffset = strongSelf.revealOffset
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
|
|||
}, currentItemNode: { [weak self] in
|
||||
return self?.galleryNode.pager.centralItemNode()
|
||||
})
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction)
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, titleView: nil)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.galleryNode.statusBar = self.statusBar
|
||||
|
|
@ -218,7 +218,7 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
|
|||
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
strongSelf.centralItemTitle.set(node.title())
|
||||
strongSelf.centralItemTitleView.set(node.titleView())
|
||||
//strongSelf.centralItemTitleView.set(node.titleView())
|
||||
strongSelf.centralItemNavigationStyle.set(node.navigationStyle())
|
||||
strongSelf.centralItemFooterContentNode.set(node.footerContent())
|
||||
}
|
||||
|
|
@ -237,7 +237,7 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo
|
|||
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let presentationArguments = self.presentationArguments as? SecureIdDocumentGalleryControllerPresentationArguments {
|
||||
self.centralItemTitle.set(centralItemNode.title())
|
||||
self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
//self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||
self.centralItemFooterContentNode.set(centralItemNode.footerContent())
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ final class SecureIdDocumentGalleryFooterContentNode: GalleryFooterContentNode {
|
|||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
let width = size.width
|
||||
var panelHeight: CGFloat = 44.0 + bottomInset
|
||||
panelHeight += contentInset
|
||||
|
|
@ -117,7 +117,7 @@ final class SecureIdDocumentGalleryFooterContentNode: GalleryFooterContentNode {
|
|||
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((width - dateSize.width) / 2.0), y: panelHeight - bottomInset - 44.0 + floor((44.0 - dateSize.height - authorNameSize.height - labelsSpacing) / 2.0) + authorNameSize.height + labelsSpacing), size: dateSize)
|
||||
}
|
||||
|
||||
return panelHeight
|
||||
return LayoutInfo(height: panelHeight, needsShadow: false)
|
||||
}
|
||||
|
||||
override func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
private var centralEntryIndex: Int?
|
||||
|
||||
private let centralItemTitle = Promise<String>()
|
||||
private let centralItemTitleView = Promise<UIView?>()
|
||||
private let centralItemTitleContent = Promise<GalleryTitleView.Content?>()
|
||||
private let centralItemRightBarButtonItems = Promise<[UIBarButtonItem]?>(nil)
|
||||
private let centralItemNavigationStyle = Promise<GalleryItemNodeNavigationStyle>()
|
||||
private let centralItemFooterContentNode = Promise<(GalleryFooterContentNode?, GalleryOverlayContentNode?)>()
|
||||
|
|
@ -427,6 +427,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
return self._hiddenMedia.get()
|
||||
}
|
||||
|
||||
private let titleView: GalleryTitleView
|
||||
|
||||
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
|
||||
|
||||
private let editDisposable = MetaDisposable ()
|
||||
|
|
@ -441,11 +443,15 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
|
||||
self.centralEntryIndex = centralEntryIndex
|
||||
|
||||
self.titleView = GalleryTitleView(context: context, presentationData: self.presentationData)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: GalleryController.darkNavigationTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
|
||||
|
||||
let backItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Back, target: self, action: #selector(self.donePressed))
|
||||
self.navigationItem.leftBarButtonItem = backItem
|
||||
|
||||
self.navigationItem.titleView = self.titleView
|
||||
|
||||
self.statusBar.statusBarStyle = .White
|
||||
|
||||
let remoteEntriesSignal: Signal<[AvatarGalleryEntry], NoError>
|
||||
|
|
@ -546,8 +552,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
}
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(self.centralItemTitleView.get().start(next: { [weak self] titleView in
|
||||
self?.navigationItem.titleView = titleView
|
||||
self.centralItemAttributesDisposable.add(self.centralItemTitleContent.get().start(next: { [weak self] titleContent in
|
||||
self?.titleView.setContent(content: titleContent)
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(self.centralItemRightBarButtonItems.get().start(next: { [weak self] rightBarButtonItems in
|
||||
|
|
@ -633,7 +639,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
}, currentItemNode: { [weak self] in
|
||||
return self?.galleryNode.pager.centralItemNode()
|
||||
})
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction)
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, titleView: self.titleView)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.galleryNode.pager.updateOnReplacement = true
|
||||
|
|
@ -680,7 +686,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
strongSelf.centralItemTitle.set(node.title())
|
||||
strongSelf.centralItemTitleView.set(node.titleView())
|
||||
strongSelf.centralItemTitleContent.set(node.titleContent())
|
||||
strongSelf.centralItemRightBarButtonItems.set(node.rightBarButtonItems())
|
||||
strongSelf.centralItemNavigationStyle.set(node.navigationStyle())
|
||||
strongSelf.centralItemFooterContentNode.set(node.footerContent())
|
||||
|
|
@ -709,7 +715,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
|
|||
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let presentationArguments = self.presentationArguments as? AvatarGalleryControllerPresentationArguments {
|
||||
self.centralItemTitle.set(centralItemNode.title())
|
||||
self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
self.centralItemTitleContent.set(centralItemNode.titleContent())
|
||||
self.centralItemRightBarButtonItems.set(centralItemNode.rightBarButtonItems())
|
||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||
self.centralItemFooterContentNode.set(centralItemNode.footerContent())
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
|||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
self.validLayout = (size, metrics, leftInset, rightInset, bottomInset, contentInset)
|
||||
|
||||
let width = size.width
|
||||
|
|
@ -203,7 +203,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
|
|||
let mainButtonSize = self.setMainButton.measure(constrainedSize)
|
||||
self.setMainButton.frame = CGRect(origin: CGPoint(x: floor((width - mainButtonSize.width) / 2.0), y: panelHeight - bottomInset - 44.0 + floor((44.0 - mainButtonSize.height) / 2.0)), size: mainButtonSize)
|
||||
|
||||
return panelHeight
|
||||
return LayoutInfo(height: panelHeight, needsShadow: false)
|
||||
}
|
||||
|
||||
override func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ private enum ArchiveSettingsControllerEntry: ItemListNodeEntry {
|
|||
case .unmutedHeader:
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.ArchiveSettings_UnmutedChatsHeader, sectionId: self.section)
|
||||
case let .unmutedValue(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: presentationData.strings.ArchiveSettings_KeepArchived, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: presentationData.strings.ArchiveSettings_KeepArchived, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateUnmuted(value)
|
||||
})
|
||||
case .unmutedFooter:
|
||||
|
|
@ -97,7 +97,7 @@ private enum ArchiveSettingsControllerEntry: ItemListNodeEntry {
|
|||
case .foldersHeader:
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.ArchiveSettings_FolderChatsHeader, sectionId: self.section)
|
||||
case let .foldersValue(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: presentationData.strings.ArchiveSettings_KeepArchived, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: presentationData.strings.ArchiveSettings_KeepArchived, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateFolders(value)
|
||||
})
|
||||
case .foldersFooter:
|
||||
|
|
@ -105,7 +105,7 @@ private enum ArchiveSettingsControllerEntry: ItemListNodeEntry {
|
|||
case .unknownHeader:
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: presentationData.strings.ArchiveSettings_UnknownChatsHeader, sectionId: self.section)
|
||||
case let .unknownValue(isOn, isLocked):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: presentationData.strings.ArchiveSettings_AutomaticallyArchive, value: isOn, enableInteractiveChanges: !isLocked, enabled: true, displayLocked: isLocked, sectionId: self.section, style: .blocks, updated: { value in
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: presentationData.strings.ArchiveSettings_AutomaticallyArchive, value: isOn, enableInteractiveChanges: !isLocked, enabled: true, displayLocked: isLocked, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.updateUnknown(value)
|
||||
}, activatedWhileDisabled: {
|
||||
arguments.updateUnknown(nil)
|
||||
|
|
|
|||
|
|
@ -911,15 +911,15 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||
return self.scrollView.convert(layer.frame, to: self.view)
|
||||
}
|
||||
|
||||
func scrollToItem(at index: Int) {
|
||||
func scrollToItem(at index: Int, force: Bool) {
|
||||
guard let layout = self.layout, let _ = self.items else {
|
||||
return
|
||||
}
|
||||
if layout.containerLayout.lockScrollingAtTop {
|
||||
if !force && layout.containerLayout.lockScrollingAtTop {
|
||||
return
|
||||
}
|
||||
let itemFrame = layout.frame(at: index)
|
||||
var contentOffset = itemFrame.minY
|
||||
var contentOffset = floor(itemFrame.midY - self.scrollView.bounds.height / 2.0)
|
||||
if contentOffset > self.scrollView.contentSize.height - self.scrollView.bounds.height {
|
||||
contentOffset = self.scrollView.contentSize.height - self.scrollView.bounds.height
|
||||
}
|
||||
|
|
@ -2234,11 +2234,11 @@ public final class SparseItemGrid: ASDisplayNode {
|
|||
return currentViewport.visualItem(at: index)
|
||||
}
|
||||
|
||||
public func scrollToItem(at index: Int) {
|
||||
public func scrollToItem(at index: Int, force: Bool = false) {
|
||||
guard let currentViewport = self.currentViewport else {
|
||||
return
|
||||
}
|
||||
currentViewport.scrollToItem(at: index)
|
||||
currentViewport.scrollToItem(at: index, force: force)
|
||||
}
|
||||
|
||||
public func ensureItemVisible(index: Int, anyAmount: Bool = true) {
|
||||
|
|
|
|||
|
|
@ -538,25 +538,26 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[736157604] = { return Api.JSONValue.parse_jsonNumber($0) }
|
||||
dict[-1715350371] = { return Api.JSONValue.parse_jsonObject($0) }
|
||||
dict[-1222740358] = { return Api.JSONValue.parse_jsonString($0) }
|
||||
dict[-916050683] = { return Api.KeyboardButton.parse_inputKeyboardButtonRequestPeer($0) }
|
||||
dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) }
|
||||
dict[-376962181] = { return Api.KeyboardButton.parse_inputKeyboardButtonUserProfile($0) }
|
||||
dict[-1560655744] = { return Api.KeyboardButton.parse_keyboardButton($0) }
|
||||
dict[-1344716869] = { return Api.KeyboardButton.parse_keyboardButtonBuy($0) }
|
||||
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
|
||||
dict[1976723854] = { return Api.KeyboardButton.parse_keyboardButtonCopy($0) }
|
||||
dict[1358175439] = { return Api.KeyboardButton.parse_keyboardButtonGame($0) }
|
||||
dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
|
||||
dict[1406648280] = { return Api.KeyboardButton.parse_keyboardButtonRequestPeer($0) }
|
||||
dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
|
||||
dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
|
||||
dict[-1598009252] = { return Api.KeyboardButton.parse_keyboardButtonSimpleWebView($0) }
|
||||
dict[-1816527947] = { return Api.KeyboardButton.parse_keyboardButtonSwitchInline($0) }
|
||||
dict[629866245] = { return Api.KeyboardButton.parse_keyboardButtonUrl($0) }
|
||||
dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) }
|
||||
dict[814112961] = { return Api.KeyboardButton.parse_keyboardButtonUserProfile($0) }
|
||||
dict[326529584] = { return Api.KeyboardButton.parse_keyboardButtonWebView($0) }
|
||||
dict[45580630] = { return Api.KeyboardButton.parse_inputKeyboardButtonRequestPeer($0) }
|
||||
dict[1744911986] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) }
|
||||
dict[2103314375] = { return Api.KeyboardButton.parse_inputKeyboardButtonUserProfile($0) }
|
||||
dict[2098662655] = { return Api.KeyboardButton.parse_keyboardButton($0) }
|
||||
dict[1067792645] = { return Api.KeyboardButton.parse_keyboardButtonBuy($0) }
|
||||
dict[-433338016] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
|
||||
dict[-1127960816] = { return Api.KeyboardButton.parse_keyboardButtonCopy($0) }
|
||||
dict[-1983540999] = { return Api.KeyboardButton.parse_keyboardButtonGame($0) }
|
||||
dict[-1438582451] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
|
||||
dict[1527715317] = { return Api.KeyboardButton.parse_keyboardButtonRequestPeer($0) }
|
||||
dict[1098841487] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
|
||||
dict[2047989634] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
|
||||
dict[-514047120] = { return Api.KeyboardButton.parse_keyboardButtonSimpleWebView($0) }
|
||||
dict[-1726768644] = { return Api.KeyboardButton.parse_keyboardButtonSwitchInline($0) }
|
||||
dict[-670292500] = { return Api.KeyboardButton.parse_keyboardButtonUrl($0) }
|
||||
dict[-183499015] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) }
|
||||
dict[-1057137399] = { return Api.KeyboardButton.parse_keyboardButtonUserProfile($0) }
|
||||
dict[-398020192] = { return Api.KeyboardButton.parse_keyboardButtonWebView($0) }
|
||||
dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) }
|
||||
dict[1339896880] = { return Api.KeyboardButtonStyle.parse_keyboardButtonStyle($0) }
|
||||
dict[-886477832] = { return Api.LabeledPrice.parse_labeledPrice($0) }
|
||||
dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) }
|
||||
dict[-288727837] = { return Api.LangPackLanguage.parse_langPackLanguage($0) }
|
||||
|
|
@ -579,6 +580,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[2055212554] = { return Api.Message.parse_messageService($0) }
|
||||
dict[-872240531] = { return Api.MessageAction.parse_messageActionBoostApply($0) }
|
||||
dict[-988359047] = { return Api.MessageAction.parse_messageActionBotAllowed($0) }
|
||||
dict[-511160261] = { return Api.MessageAction.parse_messageActionChangeCreator($0) }
|
||||
dict[-1781355374] = { return Api.MessageAction.parse_messageActionChannelCreate($0) }
|
||||
dict[-365344535] = { return Api.MessageAction.parse_messageActionChannelMigrateFrom($0) }
|
||||
dict[365886720] = { return Api.MessageAction.parse_messageActionChatAddUser($0) }
|
||||
|
|
@ -606,6 +608,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[-1281329567] = { return Api.MessageAction.parse_messageActionGroupCallScheduled($0) }
|
||||
dict[-1615153660] = { return Api.MessageAction.parse_messageActionHistoryClear($0) }
|
||||
dict[1345295095] = { return Api.MessageAction.parse_messageActionInviteToGroupCall($0) }
|
||||
dict[-1333866363] = { return Api.MessageAction.parse_messageActionNewCreatorPending($0) }
|
||||
dict[-2068281992] = { return Api.MessageAction.parse_messageActionPaidMessagesPrice($0) }
|
||||
dict[-1407246387] = { return Api.MessageAction.parse_messageActionPaidMessagesRefunded($0) }
|
||||
dict[1102307842] = { return Api.MessageAction.parse_messageActionPaymentRefunded($0) }
|
||||
|
|
@ -2016,6 +2019,8 @@ public extension Api {
|
|||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.KeyboardButtonRow:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.KeyboardButtonStyle:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.LabeledPrice:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.LangPackDifference:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -45,6 +45,57 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum KeyboardButtonStyle: TypeConstructorDescription {
|
||||
public class Cons_keyboardButtonStyle {
|
||||
public var flags: Int32
|
||||
public var icon: Int64?
|
||||
public init(flags: Int32, icon: Int64?) {
|
||||
self.flags = flags
|
||||
self.icon = icon
|
||||
}
|
||||
}
|
||||
case keyboardButtonStyle(Cons_keyboardButtonStyle)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .keyboardButtonStyle(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1339896880)
|
||||
}
|
||||
serializeInt32(_data.flags, buffer: buffer, boxed: false)
|
||||
if Int(_data.flags) & Int(1 << 3) != 0 {
|
||||
serializeInt64(_data.icon!, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .keyboardButtonStyle(let _data):
|
||||
return ("keyboardButtonStyle", [("flags", _data.flags as Any), ("icon", _data.icon as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_keyboardButtonStyle(_ reader: BufferReader) -> KeyboardButtonStyle? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {
|
||||
_2 = reader.readInt64()
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.KeyboardButtonStyle.keyboardButtonStyle(Cons_keyboardButtonStyle(flags: _1!, icon: _2))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum LabeledPrice: TypeConstructorDescription {
|
||||
public class Cons_labeledPrice {
|
||||
|
|
@ -1513,6 +1564,12 @@ public extension Api {
|
|||
self.app = app
|
||||
}
|
||||
}
|
||||
public class Cons_messageActionChangeCreator {
|
||||
public var newCreatorId: Int64
|
||||
public init(newCreatorId: Int64) {
|
||||
self.newCreatorId = newCreatorId
|
||||
}
|
||||
}
|
||||
public class Cons_messageActionChannelCreate {
|
||||
public var title: String
|
||||
public init(title: String) {
|
||||
|
|
@ -1725,6 +1782,12 @@ public extension Api {
|
|||
self.users = users
|
||||
}
|
||||
}
|
||||
public class Cons_messageActionNewCreatorPending {
|
||||
public var newCreatorId: Int64
|
||||
public init(newCreatorId: Int64) {
|
||||
self.newCreatorId = newCreatorId
|
||||
}
|
||||
}
|
||||
public class Cons_messageActionPaidMessagesPrice {
|
||||
public var flags: Int32
|
||||
public var stars: Int64
|
||||
|
|
@ -2043,6 +2106,7 @@ public extension Api {
|
|||
}
|
||||
case messageActionBoostApply(Cons_messageActionBoostApply)
|
||||
case messageActionBotAllowed(Cons_messageActionBotAllowed)
|
||||
case messageActionChangeCreator(Cons_messageActionChangeCreator)
|
||||
case messageActionChannelCreate(Cons_messageActionChannelCreate)
|
||||
case messageActionChannelMigrateFrom(Cons_messageActionChannelMigrateFrom)
|
||||
case messageActionChatAddUser(Cons_messageActionChatAddUser)
|
||||
|
|
@ -2070,6 +2134,7 @@ public extension Api {
|
|||
case messageActionGroupCallScheduled(Cons_messageActionGroupCallScheduled)
|
||||
case messageActionHistoryClear
|
||||
case messageActionInviteToGroupCall(Cons_messageActionInviteToGroupCall)
|
||||
case messageActionNewCreatorPending(Cons_messageActionNewCreatorPending)
|
||||
case messageActionPaidMessagesPrice(Cons_messageActionPaidMessagesPrice)
|
||||
case messageActionPaidMessagesRefunded(Cons_messageActionPaidMessagesRefunded)
|
||||
case messageActionPaymentRefunded(Cons_messageActionPaymentRefunded)
|
||||
|
|
@ -2122,6 +2187,12 @@ public extension Api {
|
|||
_data.app!.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .messageActionChangeCreator(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-511160261)
|
||||
}
|
||||
serializeInt64(_data.newCreatorId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageActionChannelCreate(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1781355374)
|
||||
|
|
@ -2369,6 +2440,12 @@ public extension Api {
|
|||
serializeInt64(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
break
|
||||
case .messageActionNewCreatorPending(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1333866363)
|
||||
}
|
||||
serializeInt64(_data.newCreatorId, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageActionPaidMessagesPrice(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2068281992)
|
||||
|
|
@ -2737,6 +2814,8 @@ public extension Api {
|
|||
return ("messageActionBoostApply", [("boosts", _data.boosts as Any)])
|
||||
case .messageActionBotAllowed(let _data):
|
||||
return ("messageActionBotAllowed", [("flags", _data.flags as Any), ("domain", _data.domain as Any), ("app", _data.app as Any)])
|
||||
case .messageActionChangeCreator(let _data):
|
||||
return ("messageActionChangeCreator", [("newCreatorId", _data.newCreatorId as Any)])
|
||||
case .messageActionChannelCreate(let _data):
|
||||
return ("messageActionChannelCreate", [("title", _data.title as Any)])
|
||||
case .messageActionChannelMigrateFrom(let _data):
|
||||
|
|
@ -2791,6 +2870,8 @@ public extension Api {
|
|||
return ("messageActionHistoryClear", [])
|
||||
case .messageActionInviteToGroupCall(let _data):
|
||||
return ("messageActionInviteToGroupCall", [("call", _data.call as Any), ("users", _data.users as Any)])
|
||||
case .messageActionNewCreatorPending(let _data):
|
||||
return ("messageActionNewCreatorPending", [("newCreatorId", _data.newCreatorId as Any)])
|
||||
case .messageActionPaidMessagesPrice(let _data):
|
||||
return ("messageActionPaidMessagesPrice", [("flags", _data.flags as Any), ("stars", _data.stars as Any)])
|
||||
case .messageActionPaidMessagesRefunded(let _data):
|
||||
|
|
@ -2890,6 +2971,17 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageActionChangeCreator(_ reader: BufferReader) -> MessageAction? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.MessageAction.messageActionChangeCreator(Cons_messageActionChangeCreator(newCreatorId: _1!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageActionChannelCreate(_ reader: BufferReader) -> MessageAction? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
|
|
@ -3327,6 +3419,17 @@ public extension Api {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageActionNewCreatorPending(_ reader: BufferReader) -> MessageAction? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.MessageAction.messageActionNewCreatorPending(Cons_messageActionNewCreatorPending(newCreatorId: _1!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageActionPaidMessagesPrice(_ reader: BufferReader) -> MessageAction? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
|
|
|||
|
|
@ -26,141 +26,141 @@ extension ReplyMarkupButtonAction.PeerTypes {
|
|||
extension ReplyMarkupButton {
|
||||
init(apiButton: Api.KeyboardButton) {
|
||||
switch apiButton {
|
||||
case let .keyboardButton(keyboardButtonData):
|
||||
let text = keyboardButtonData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .text)
|
||||
case let .keyboardButtonCallback(keyboardButtonCallbackData):
|
||||
let (flags, text, data) = (keyboardButtonCallbackData.flags, keyboardButtonCallbackData.text, keyboardButtonCallbackData.data)
|
||||
let memory = malloc(data.size)!
|
||||
memcpy(memory, data.data, data.size)
|
||||
let dataBuffer = MemoryBuffer(memory: memory, capacity: data.size, length: data.size, freeWhenDone: true)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .callback(requiresPassword: (flags & (1 << 0)) != 0, data: dataBuffer))
|
||||
case let .keyboardButtonRequestGeoLocation(keyboardButtonRequestGeoLocationData):
|
||||
let text = keyboardButtonRequestGeoLocationData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestMap)
|
||||
case let .keyboardButtonRequestPhone(keyboardButtonRequestPhoneData):
|
||||
let text = keyboardButtonRequestPhoneData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestPhone)
|
||||
case let .keyboardButtonSwitchInline(keyboardButtonSwitchInlineData):
|
||||
let (flags, text, query, types) = (keyboardButtonSwitchInlineData.flags, keyboardButtonSwitchInlineData.text, keyboardButtonSwitchInlineData.query, keyboardButtonSwitchInlineData.peerTypes)
|
||||
var peerTypes = ReplyMarkupButtonAction.PeerTypes()
|
||||
if let types = types {
|
||||
for type in types {
|
||||
switch type {
|
||||
case .inlineQueryPeerTypePM:
|
||||
peerTypes.insert(.users)
|
||||
case .inlineQueryPeerTypeBotPM:
|
||||
peerTypes.insert(.bots)
|
||||
case .inlineQueryPeerTypeBroadcast:
|
||||
peerTypes.insert(.channels)
|
||||
case .inlineQueryPeerTypeChat, .inlineQueryPeerTypeMegagroup:
|
||||
peerTypes.insert(.groups)
|
||||
case .inlineQueryPeerTypeSameBotPM:
|
||||
break
|
||||
}
|
||||
case let .keyboardButton(keyboardButtonData):
|
||||
let text = keyboardButtonData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .text, style: keyboardButtonData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonCallback(keyboardButtonCallbackData):
|
||||
let (flags, text, data) = (keyboardButtonCallbackData.flags, keyboardButtonCallbackData.text, keyboardButtonCallbackData.data)
|
||||
let memory = malloc(data.size)!
|
||||
memcpy(memory, data.data, data.size)
|
||||
let dataBuffer = MemoryBuffer(memory: memory, capacity: data.size, length: data.size, freeWhenDone: true)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .callback(requiresPassword: (flags & (1 << 0)) != 0, data: dataBuffer), style: keyboardButtonCallbackData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonRequestGeoLocation(keyboardButtonRequestGeoLocationData):
|
||||
let text = keyboardButtonRequestGeoLocationData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestMap, style: keyboardButtonRequestGeoLocationData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonRequestPhone(keyboardButtonRequestPhoneData):
|
||||
let text = keyboardButtonRequestPhoneData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestPhone, style: keyboardButtonRequestPhoneData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonSwitchInline(keyboardButtonSwitchInlineData):
|
||||
let (flags, text, query, types) = (keyboardButtonSwitchInlineData.flags, keyboardButtonSwitchInlineData.text, keyboardButtonSwitchInlineData.query, keyboardButtonSwitchInlineData.peerTypes)
|
||||
var peerTypes = ReplyMarkupButtonAction.PeerTypes()
|
||||
if let types = types {
|
||||
for type in types {
|
||||
switch type {
|
||||
case .inlineQueryPeerTypePM:
|
||||
peerTypes.insert(.users)
|
||||
case .inlineQueryPeerTypeBotPM:
|
||||
peerTypes.insert(.bots)
|
||||
case .inlineQueryPeerTypeBroadcast:
|
||||
peerTypes.insert(.channels)
|
||||
case .inlineQueryPeerTypeChat, .inlineQueryPeerTypeMegagroup:
|
||||
peerTypes.insert(.groups)
|
||||
case .inlineQueryPeerTypeSameBotPM:
|
||||
break
|
||||
}
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .switchInline(samePeer: (flags & (1 << 0)) != 0, query: query, peerTypes: peerTypes))
|
||||
case let .keyboardButtonUrl(keyboardButtonUrlData):
|
||||
let (text, url) = (keyboardButtonUrlData.text, keyboardButtonUrlData.url)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .url(url))
|
||||
case let .keyboardButtonGame(keyboardButtonGameData):
|
||||
let text = keyboardButtonGameData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openWebApp)
|
||||
case let .keyboardButtonBuy(keyboardButtonBuyData):
|
||||
let text = keyboardButtonBuyData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .payment)
|
||||
case let .keyboardButtonUrlAuth(keyboardButtonUrlAuthData):
|
||||
let (text, fwdText, url, buttonId) = (keyboardButtonUrlAuthData.text, keyboardButtonUrlAuthData.fwdText, keyboardButtonUrlAuthData.url, keyboardButtonUrlAuthData.buttonId)
|
||||
self.init(title: text, titleWhenForwarded: fwdText, action: .urlAuth(url: url, buttonId: buttonId))
|
||||
case let .inputKeyboardButtonUrlAuth(inputKeyboardButtonUrlAuthData):
|
||||
let (text, fwdText, url) = (inputKeyboardButtonUrlAuthData.text, inputKeyboardButtonUrlAuthData.fwdText, inputKeyboardButtonUrlAuthData.url)
|
||||
self.init(title: text, titleWhenForwarded: fwdText, action: .urlAuth(url: url, buttonId: 0))
|
||||
case let .keyboardButtonRequestPoll(keyboardButtonRequestPollData):
|
||||
let (quiz, text) = (keyboardButtonRequestPollData.quiz, keyboardButtonRequestPollData.text)
|
||||
let isQuiz: Bool? = quiz.flatMap { quiz in
|
||||
if case .boolTrue = quiz {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .switchInline(samePeer: (flags & (1 << 0)) != 0, query: query, peerTypes: peerTypes), style: keyboardButtonSwitchInlineData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonUrl(keyboardButtonUrlData):
|
||||
let (text, url) = (keyboardButtonUrlData.text, keyboardButtonUrlData.url)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .url(url), style: keyboardButtonUrlData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonGame(keyboardButtonGameData):
|
||||
let text = keyboardButtonGameData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openWebApp, style: keyboardButtonGameData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonBuy(keyboardButtonBuyData):
|
||||
let text = keyboardButtonBuyData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .payment, style: keyboardButtonBuyData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonUrlAuth(keyboardButtonUrlAuthData):
|
||||
let (text, fwdText, url, buttonId) = (keyboardButtonUrlAuthData.text, keyboardButtonUrlAuthData.fwdText, keyboardButtonUrlAuthData.url, keyboardButtonUrlAuthData.buttonId)
|
||||
self.init(title: text, titleWhenForwarded: fwdText, action: .urlAuth(url: url, buttonId: buttonId), style: keyboardButtonUrlAuthData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .inputKeyboardButtonUrlAuth(inputKeyboardButtonUrlAuthData):
|
||||
let (text, fwdText, url) = (inputKeyboardButtonUrlAuthData.text, inputKeyboardButtonUrlAuthData.fwdText, inputKeyboardButtonUrlAuthData.url)
|
||||
self.init(title: text, titleWhenForwarded: fwdText, action: .urlAuth(url: url, buttonId: 0), style: inputKeyboardButtonUrlAuthData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonRequestPoll(keyboardButtonRequestPollData):
|
||||
let (quiz, text) = (keyboardButtonRequestPollData.quiz, keyboardButtonRequestPollData.text)
|
||||
let isQuiz: Bool? = quiz.flatMap { quiz in
|
||||
if case .boolTrue = quiz {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .setupPoll(isQuiz: isQuiz))
|
||||
case let .keyboardButtonUserProfile(keyboardButtonUserProfileData):
|
||||
let (text, userId) = (keyboardButtonUserProfileData.text, keyboardButtonUserProfileData.userId)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openUserProfile(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))))
|
||||
case let .inputKeyboardButtonUserProfile(inputKeyboardButtonUserProfileData):
|
||||
let text = inputKeyboardButtonUserProfileData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openUserProfile(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0))))
|
||||
case let .keyboardButtonWebView(keyboardButtonWebViewData):
|
||||
let (text, url) = (keyboardButtonWebViewData.text, keyboardButtonWebViewData.url)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: false))
|
||||
case let .keyboardButtonSimpleWebView(keyboardButtonSimpleWebViewData):
|
||||
let (text, url) = (keyboardButtonSimpleWebViewData.text, keyboardButtonSimpleWebViewData.url)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: true))
|
||||
case let .keyboardButtonRequestPeer(keyboardButtonRequestPeerData):
|
||||
let (text, buttonId, peerType, maxQuantity) = (keyboardButtonRequestPeerData.text, keyboardButtonRequestPeerData.buttonId, keyboardButtonRequestPeerData.peerType, keyboardButtonRequestPeerData.maxQuantity)
|
||||
let mappedPeerType: ReplyMarkupButtonRequestPeerType
|
||||
switch peerType {
|
||||
case let .requestPeerTypeUser(requestPeerTypeUserData):
|
||||
let (bot, premium) = (requestPeerTypeUserData.bot, requestPeerTypeUserData.premium)
|
||||
mappedPeerType = .user(ReplyMarkupButtonRequestPeerType.User(
|
||||
isBot: bot.flatMap({ $0 == .boolTrue }),
|
||||
isPremium: premium.flatMap({ $0 == .boolTrue })
|
||||
))
|
||||
case let .requestPeerTypeChat(requestPeerTypeChatData):
|
||||
let (flags, hasUsername, forum, userAdminRights, botAdminRights) = (requestPeerTypeChatData.flags, requestPeerTypeChatData.hasUsername, requestPeerTypeChatData.forum, requestPeerTypeChatData.userAdminRights, requestPeerTypeChatData.botAdminRights)
|
||||
mappedPeerType = .group(ReplyMarkupButtonRequestPeerType.Group(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
isForum: forum.flatMap({ $0 == .boolTrue }),
|
||||
botParticipant: (flags & (1 << 5)) != 0,
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
case let .requestPeerTypeBroadcast(requestPeerTypeBroadcastData):
|
||||
let (flags, hasUsername, userAdminRights, botAdminRights) = (requestPeerTypeBroadcastData.flags, requestPeerTypeBroadcastData.hasUsername, requestPeerTypeBroadcastData.userAdminRights, requestPeerTypeBroadcastData.botAdminRights)
|
||||
mappedPeerType = .channel(ReplyMarkupButtonRequestPeerType.Channel(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestPeer(peerType: mappedPeerType, buttonId: buttonId, maxQuantity: maxQuantity))
|
||||
case let .inputKeyboardButtonRequestPeer(inputKeyboardButtonRequestPeerData):
|
||||
let (text, buttonId, peerType, maxQuantity) = (inputKeyboardButtonRequestPeerData.text, inputKeyboardButtonRequestPeerData.buttonId, inputKeyboardButtonRequestPeerData.peerType, inputKeyboardButtonRequestPeerData.maxQuantity)
|
||||
let mappedPeerType: ReplyMarkupButtonRequestPeerType
|
||||
switch peerType {
|
||||
case let .requestPeerTypeUser(requestPeerTypeUserData):
|
||||
let (bot, premium) = (requestPeerTypeUserData.bot, requestPeerTypeUserData.premium)
|
||||
mappedPeerType = .user(ReplyMarkupButtonRequestPeerType.User(
|
||||
isBot: bot.flatMap({ $0 == .boolTrue }),
|
||||
isPremium: premium.flatMap({ $0 == .boolTrue })
|
||||
))
|
||||
case let .requestPeerTypeChat(requestPeerTypeChatData):
|
||||
let (flags, hasUsername, forum, userAdminRights, botAdminRights) = (requestPeerTypeChatData.flags, requestPeerTypeChatData.hasUsername, requestPeerTypeChatData.forum, requestPeerTypeChatData.userAdminRights, requestPeerTypeChatData.botAdminRights)
|
||||
mappedPeerType = .group(ReplyMarkupButtonRequestPeerType.Group(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
isForum: forum.flatMap({ $0 == .boolTrue }),
|
||||
botParticipant: (flags & (1 << 5)) != 0,
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
case let .requestPeerTypeBroadcast(requestPeerTypeBroadcastData):
|
||||
let (flags, hasUsername, userAdminRights, botAdminRights) = (requestPeerTypeBroadcastData.flags, requestPeerTypeBroadcastData.hasUsername, requestPeerTypeBroadcastData.userAdminRights, requestPeerTypeBroadcastData.botAdminRights)
|
||||
mappedPeerType = .channel(ReplyMarkupButtonRequestPeerType.Channel(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestPeer(peerType: mappedPeerType, buttonId: buttonId, maxQuantity: maxQuantity))
|
||||
case let .keyboardButtonCopy(keyboardButtonCopyData):
|
||||
let (text, payload) = (keyboardButtonCopyData.text, keyboardButtonCopyData.copyText)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .copyText(payload: payload))
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .setupPoll(isQuiz: isQuiz), style: keyboardButtonRequestPollData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonUserProfile(keyboardButtonUserProfileData):
|
||||
let (text, userId) = (keyboardButtonUserProfileData.text, keyboardButtonUserProfileData.userId)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openUserProfile(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))), style: keyboardButtonUserProfileData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .inputKeyboardButtonUserProfile(inputKeyboardButtonUserProfileData):
|
||||
let text = inputKeyboardButtonUserProfileData.text
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openUserProfile(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0))), style: inputKeyboardButtonUserProfileData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonWebView(keyboardButtonWebViewData):
|
||||
let (text, url) = (keyboardButtonWebViewData.text, keyboardButtonWebViewData.url)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: false), style: keyboardButtonWebViewData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonSimpleWebView(keyboardButtonSimpleWebViewData):
|
||||
let (text, url) = (keyboardButtonSimpleWebViewData.text, keyboardButtonSimpleWebViewData.url)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .openWebView(url: url, simple: true), style: keyboardButtonSimpleWebViewData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonRequestPeer(keyboardButtonRequestPeerData):
|
||||
let (text, buttonId, peerType, maxQuantity) = (keyboardButtonRequestPeerData.text, keyboardButtonRequestPeerData.buttonId, keyboardButtonRequestPeerData.peerType, keyboardButtonRequestPeerData.maxQuantity)
|
||||
let mappedPeerType: ReplyMarkupButtonRequestPeerType
|
||||
switch peerType {
|
||||
case let .requestPeerTypeUser(requestPeerTypeUserData):
|
||||
let (bot, premium) = (requestPeerTypeUserData.bot, requestPeerTypeUserData.premium)
|
||||
mappedPeerType = .user(ReplyMarkupButtonRequestPeerType.User(
|
||||
isBot: bot.flatMap({ $0 == .boolTrue }),
|
||||
isPremium: premium.flatMap({ $0 == .boolTrue })
|
||||
))
|
||||
case let .requestPeerTypeChat(requestPeerTypeChatData):
|
||||
let (flags, hasUsername, forum, userAdminRights, botAdminRights) = (requestPeerTypeChatData.flags, requestPeerTypeChatData.hasUsername, requestPeerTypeChatData.forum, requestPeerTypeChatData.userAdminRights, requestPeerTypeChatData.botAdminRights)
|
||||
mappedPeerType = .group(ReplyMarkupButtonRequestPeerType.Group(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
isForum: forum.flatMap({ $0 == .boolTrue }),
|
||||
botParticipant: (flags & (1 << 5)) != 0,
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
case let .requestPeerTypeBroadcast(requestPeerTypeBroadcastData):
|
||||
let (flags, hasUsername, userAdminRights, botAdminRights) = (requestPeerTypeBroadcastData.flags, requestPeerTypeBroadcastData.hasUsername, requestPeerTypeBroadcastData.userAdminRights, requestPeerTypeBroadcastData.botAdminRights)
|
||||
mappedPeerType = .channel(ReplyMarkupButtonRequestPeerType.Channel(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestPeer(peerType: mappedPeerType, buttonId: buttonId, maxQuantity: maxQuantity), style: keyboardButtonRequestPeerData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .inputKeyboardButtonRequestPeer(inputKeyboardButtonRequestPeerData):
|
||||
let (text, buttonId, peerType, maxQuantity) = (inputKeyboardButtonRequestPeerData.text, inputKeyboardButtonRequestPeerData.buttonId, inputKeyboardButtonRequestPeerData.peerType, inputKeyboardButtonRequestPeerData.maxQuantity)
|
||||
let mappedPeerType: ReplyMarkupButtonRequestPeerType
|
||||
switch peerType {
|
||||
case let .requestPeerTypeUser(requestPeerTypeUserData):
|
||||
let (bot, premium) = (requestPeerTypeUserData.bot, requestPeerTypeUserData.premium)
|
||||
mappedPeerType = .user(ReplyMarkupButtonRequestPeerType.User(
|
||||
isBot: bot.flatMap({ $0 == .boolTrue }),
|
||||
isPremium: premium.flatMap({ $0 == .boolTrue })
|
||||
))
|
||||
case let .requestPeerTypeChat(requestPeerTypeChatData):
|
||||
let (flags, hasUsername, forum, userAdminRights, botAdminRights) = (requestPeerTypeChatData.flags, requestPeerTypeChatData.hasUsername, requestPeerTypeChatData.forum, requestPeerTypeChatData.userAdminRights, requestPeerTypeChatData.botAdminRights)
|
||||
mappedPeerType = .group(ReplyMarkupButtonRequestPeerType.Group(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
isForum: forum.flatMap({ $0 == .boolTrue }),
|
||||
botParticipant: (flags & (1 << 5)) != 0,
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
case let .requestPeerTypeBroadcast(requestPeerTypeBroadcastData):
|
||||
let (flags, hasUsername, userAdminRights, botAdminRights) = (requestPeerTypeBroadcastData.flags, requestPeerTypeBroadcastData.hasUsername, requestPeerTypeBroadcastData.userAdminRights, requestPeerTypeBroadcastData.botAdminRights)
|
||||
mappedPeerType = .channel(ReplyMarkupButtonRequestPeerType.Channel(
|
||||
isCreator: (flags & (1 << 0)) != 0,
|
||||
hasUsername: hasUsername.flatMap({ $0 == .boolTrue }),
|
||||
userAdminRights: userAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:)),
|
||||
botAdminRights: botAdminRights.flatMap(TelegramChatAdminRights.init(apiAdminRights:))
|
||||
))
|
||||
}
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestPeer(peerType: mappedPeerType, buttonId: buttonId, maxQuantity: maxQuantity), style: inputKeyboardButtonRequestPeerData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
case let .keyboardButtonCopy(keyboardButtonCopyData):
|
||||
let (text, payload) = (keyboardButtonCopyData.text, keyboardButtonCopyData.copyText)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .copyText(payload: payload), style: keyboardButtonCopyData.style.flatMap(ReplyMarkupButton.Style.init(apiStyle:)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -292,6 +292,10 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
|||
if let otherParticipants {
|
||||
result.append(contentsOf: otherParticipants.map(\.peerId))
|
||||
}
|
||||
case let .messageActionNewCreatorPending(messageActionNewCreatorPending):
|
||||
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(messageActionNewCreatorPending.newCreatorId)))
|
||||
case let .messageActionChangeCreator(messageActionChangeCreator):
|
||||
result.append(PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(messageActionChangeCreator.newCreatorId)))
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -349,6 +349,16 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||
return nil
|
||||
}
|
||||
return TelegramMediaAction(action: .starGiftPurchaseOfferDeclined(gift: gift, amount: CurrencyAmount(apiAmount: price), hasExpired: (flags & (1 << 0)) != 0))
|
||||
case let .messageActionNewCreatorPending(messageActionNewCreatorPending):
|
||||
return TelegramMediaAction(action: .groupCreatorChange(TelegramMediaActionType.GroupCreatorChange(
|
||||
kind: .pending,
|
||||
targetPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(messageActionNewCreatorPending.newCreatorId))
|
||||
)))
|
||||
case let .messageActionChangeCreator(messageActionChangeCreator):
|
||||
return TelegramMediaAction(action: .groupCreatorChange(TelegramMediaActionType.GroupCreatorChange(
|
||||
kind: .applied,
|
||||
targetPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(messageActionChangeCreator.newCreatorId))
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -932,7 +932,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
|
|||
buttons.append(button)
|
||||
} else if case let .switchInline(samePeer, query, peerTypes) = button.action, sourceSentViaBot {
|
||||
let samePeer = samePeer && peerId == sourceMessage.id.peerId
|
||||
let updatedButton = ReplyMarkupButton(title: button.titleWhenForwarded ?? button.title, titleWhenForwarded: button.titleWhenForwarded, action: .switchInline(samePeer: samePeer, query: query, peerTypes: peerTypes))
|
||||
let updatedButton = ReplyMarkupButton(title: button.titleWhenForwarded ?? button.title, titleWhenForwarded: button.titleWhenForwarded, action: .switchInline(samePeer: samePeer, query: query, peerTypes: peerTypes), style: button.style)
|
||||
buttons.append(updatedButton)
|
||||
} else {
|
||||
rows.removeAll()
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
|||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 223
|
||||
return 224
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import Postbox
|
||||
import TelegramApi
|
||||
|
||||
public enum ReplyMarkupButtonRequestPeerType: Codable, Equatable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
|
|
@ -323,21 +324,79 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
extension ReplyMarkupButton.Style {
|
||||
init(apiStyle: Api.KeyboardButtonStyle) {
|
||||
switch apiStyle {
|
||||
case let .keyboardButtonStyle(keyboardButtonStyle):
|
||||
var color: Color?
|
||||
|
||||
if keyboardButtonStyle.flags & (1 << 0) != 0 {
|
||||
color = .primary
|
||||
} else if keyboardButtonStyle.flags & (1 << 1) != 0 {
|
||||
color = .danger
|
||||
} else if keyboardButtonStyle.flags & (1 << 2) != 0 {
|
||||
color = .success
|
||||
}
|
||||
|
||||
self.init(color: color, iconFileId: keyboardButtonStyle.icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ReplyMarkupButton: PostboxCoding, Equatable {
|
||||
public struct Style: PostboxCoding, Equatable {
|
||||
public enum Color: Int32 {
|
||||
case primary = 0
|
||||
case danger = 1
|
||||
case success = 2
|
||||
}
|
||||
|
||||
public let color: Color?
|
||||
public let iconFileId: Int64?
|
||||
|
||||
public init(color: Color?, iconFileId: Int64?) {
|
||||
self.color = color
|
||||
self.iconFileId = iconFileId
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
let rawColor = decoder.decodeInt32ForKey("c", orElse: -1)
|
||||
if rawColor != -1 {
|
||||
self.color = Color(rawValue: rawColor)
|
||||
} else {
|
||||
self.color = nil
|
||||
}
|
||||
|
||||
self.iconFileId = decoder.decodeOptionalInt64ForKey("i")
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
encoder.encodeInt32(self.color?.rawValue ?? -1, forKey: "c")
|
||||
if let iconFileId = self.iconFileId {
|
||||
encoder.encodeInt64(iconFileId, forKey: "i")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "i")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public let title: String
|
||||
public let titleWhenForwarded: String?
|
||||
public let action: ReplyMarkupButtonAction
|
||||
public let style: Style?
|
||||
|
||||
public init(title: String, titleWhenForwarded: String?, action: ReplyMarkupButtonAction) {
|
||||
public init(title: String, titleWhenForwarded: String?, action: ReplyMarkupButtonAction, style: Style?) {
|
||||
self.title = title
|
||||
self.titleWhenForwarded = titleWhenForwarded
|
||||
self.action = action
|
||||
self.style = style
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
self.title = decoder.decodeStringForKey(".t", orElse: "")
|
||||
self.titleWhenForwarded = decoder.decodeOptionalStringForKey(".tf")
|
||||
self.action = ReplyMarkupButtonAction(decoder: decoder)
|
||||
self.style = (decoder.decodeObjectForKey(".stl", decoder: { Style(decoder: $0) }) as? Style)
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
|
|
@ -348,10 +407,15 @@ public struct ReplyMarkupButton: PostboxCoding, Equatable {
|
|||
encoder.encodeNil(forKey: ".tf")
|
||||
}
|
||||
self.action.encode(encoder)
|
||||
if let style = self.style {
|
||||
encoder.encodeObject(style, forKey: ".stl")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: ".stl")
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: ReplyMarkupButton, rhs: ReplyMarkupButton) -> Bool {
|
||||
return lhs.title == rhs.title && lhs.action == rhs.action
|
||||
return lhs.title == rhs.title && lhs.action == rhs.action && lhs.style == rhs.style
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -399,6 +463,21 @@ public class ReplyMarkupMessageAttribute: MessageAttribute, Equatable {
|
|||
public let flags: ReplyMarkupMessageFlags
|
||||
public let placeholder: String?
|
||||
|
||||
public var associatedMediaIds: [MediaId] {
|
||||
var result: [MediaId] = []
|
||||
for row in rows {
|
||||
for button in row.buttons {
|
||||
if let iconFileId = button.style?.iconFileId {
|
||||
let mediaId = MediaId(namespace: Namespaces.Media.CloudFile, id: iconFileId)
|
||||
if !result.contains(mediaId) {
|
||||
result.append(mediaId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public init(rows: [ReplyMarkupRow], flags: ReplyMarkupMessageFlags, placeholder: String?) {
|
||||
self.rows = rows
|
||||
self.flags = flags
|
||||
|
|
|
|||
|
|
@ -211,6 +211,38 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
public static let isFinal = StarGiftAuctionBidFlags(rawValue: 1 << 3)
|
||||
}
|
||||
|
||||
public struct GroupCreatorChange: Codable, Equatable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case kind = "k"
|
||||
case targetPeerId = "t"
|
||||
}
|
||||
|
||||
public enum Kind: Int32 {
|
||||
case pending = 0
|
||||
case applied = 1
|
||||
}
|
||||
|
||||
public let kind: Kind
|
||||
public let targetPeerId: PeerId
|
||||
|
||||
public init(kind: Kind, targetPeerId: PeerId) {
|
||||
self.kind = kind
|
||||
self.targetPeerId = targetPeerId
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.kind = Kind(rawValue: try container.decode(Int32.self, forKey: .kind)) ?? .pending
|
||||
self.targetPeerId = PeerId(try container.decode(Int64.self, forKey: .targetPeerId))
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.kind.rawValue, forKey: .kind)
|
||||
try container.encode(self.targetPeerId.toInt64(), forKey: .kind)
|
||||
}
|
||||
}
|
||||
|
||||
case unknown
|
||||
case groupCreated(title: String)
|
||||
case addedMembers(peerIds: [PeerId])
|
||||
|
|
@ -269,7 +301,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
case suggestedBirthday(TelegramBirthday)
|
||||
case starGiftPurchaseOffer(gift: StarGift, amount: CurrencyAmount, expireDate: Int32, isAccepted: Bool, isDeclined: Bool)
|
||||
case starGiftPurchaseOfferDeclined(gift: StarGift, amount: CurrencyAmount, hasExpired: Bool)
|
||||
case starGiftCraftFail
|
||||
case groupCreatorChange(GroupCreatorChange)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0)
|
||||
|
|
@ -440,9 +472,9 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
case 56:
|
||||
self = .starGiftPurchaseOffer(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount") ?? CurrencyAmount(amount: .zero, currency: .stars), expireDate: decoder.decodeInt32ForKey("expireDate", orElse: 0), isAccepted: decoder.decodeBoolForKey("isAccepted", orElse: false), isDeclined: decoder.decodeBoolForKey("isDeclined", orElse: false))
|
||||
case 57:
|
||||
self = .starGiftPurchaseOfferDeclined(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount") ?? CurrencyAmount(amount: .zero, currency: .stars), hasExpired: decoder.decodeBoolForKey("hasExpired", orElse: false))
|
||||
case 58:
|
||||
self = .starGiftCraftFail
|
||||
self = .starGiftPurchaseOfferDeclined(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, amount: decoder.decodeCodable(CurrencyAmount.self, forKey: "amount")!, hasExpired: decoder.decodeBoolForKey("hasExpired", orElse: false))
|
||||
case 59:
|
||||
self = .groupCreatorChange(decoder.decodeCodable(GroupCreatorChange.self, forKey: "d") ?? GroupCreatorChange(kind: .pending, targetPeerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(0))))
|
||||
default:
|
||||
self = .unknown
|
||||
}
|
||||
|
|
@ -934,8 +966,9 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
encoder.encodeObject(gift, forKey: "gift")
|
||||
encoder.encodeCodable(amount, forKey: "amount")
|
||||
encoder.encodeBool(hasExpired, forKey: "hasExpired")
|
||||
case .starGiftCraftFail:
|
||||
encoder.encodeInt32(58, forKey: "_rawValue")
|
||||
case let .groupCreatorChange(groupCreatorChange):
|
||||
encoder.encodeInt32(59, forKey: "_rawValue")
|
||||
encoder.encodeCodable(groupCreatorChange, forKey: "d")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -992,6 +1025,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
return peerIds
|
||||
case let .conferenceCall(conferenceCall):
|
||||
return conferenceCall.otherParticipants
|
||||
case let .groupCreatorChange(groupCreatorChange):
|
||||
return [groupCreatorChange.targetPeerId]
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,6 +199,11 @@ public enum MessageActionUrlAuthResult {
|
|||
case request(domain: String, bot: Peer, clientData: ClientData?, flags: Flags)
|
||||
}
|
||||
|
||||
public enum MessageActionUrlAuthError {
|
||||
case generic
|
||||
case urlExpired
|
||||
}
|
||||
|
||||
public enum MessageActionUrlSubject {
|
||||
case message(id: MessageId, buttonId: Int32)
|
||||
case url(String)
|
||||
|
|
@ -259,7 +264,7 @@ func _internal_requestMessageActionUrlAuth(account: Account, subject: MessageAct
|
|||
}
|
||||
}
|
||||
|
||||
func _internal_acceptMessageActionUrlAuth(account: Account, subject: MessageActionUrlSubject, allowWriteAccess: Bool, sharePhoneNumber: Bool) -> Signal<MessageActionUrlAuthResult, NoError> {
|
||||
func _internal_acceptMessageActionUrlAuth(account: Account, subject: MessageActionUrlSubject, allowWriteAccess: Bool, sharePhoneNumber: Bool) -> Signal<MessageActionUrlAuthResult, MessageActionUrlAuthError> {
|
||||
var flags: Int32 = 0
|
||||
if allowWriteAccess {
|
||||
flags |= Int32(1 << 0)
|
||||
|
|
@ -292,13 +297,10 @@ func _internal_acceptMessageActionUrlAuth(account: Account, subject: MessageActi
|
|||
|
||||
|
||||
return request
|
||||
|> `catch` { _ -> Signal<Api.UrlAuthResult?, NoError> in
|
||||
return .single(nil)
|
||||
|> mapError { _ -> MessageActionUrlAuthError in
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> MessageActionUrlAuthResult in
|
||||
guard let result = result else {
|
||||
return .default
|
||||
}
|
||||
switch result {
|
||||
case let .urlAuthResultAccepted(urlAuthResultAcceptedData):
|
||||
let url = urlAuthResultAcceptedData.url
|
||||
|
|
|
|||
|
|
@ -86,13 +86,14 @@ public final class SparseMessageList {
|
|||
}
|
||||
private let loadHoleDisposable = MetaDisposable()
|
||||
private var loadingHole: LoadingHole?
|
||||
private var isLoadingInitial: Bool = false
|
||||
|
||||
private var loadingPlaceholders: [MessageId: Disposable] = [:]
|
||||
private var loadedPlaceholders: [MessageId: Message] = [:]
|
||||
|
||||
let statePromise = Promise<SparseMessageList.State>()
|
||||
|
||||
init(queue: Queue, account: Account, peerId: PeerId, threadId: Int64?, messageTag: MessageTags) {
|
||||
init(queue: Queue, account: Account, peerId: PeerId, threadId: Int64?, messageTag: MessageTags, initialMessageIndex: MessageIndex?) {
|
||||
self.queue = queue
|
||||
self.account = account
|
||||
self.peerId = peerId
|
||||
|
|
@ -102,6 +103,11 @@ public final class SparseMessageList {
|
|||
self.resetTopSection()
|
||||
|
||||
if self.threadId == nil {
|
||||
if initialMessageIndex != nil {
|
||||
self.isLoadingInitial = true
|
||||
self.updateState()
|
||||
}
|
||||
|
||||
self.sparseItemsDisposable = (self.account.postbox.transaction { transaction -> Api.InputPeer? in
|
||||
return transaction.getPeer(peerId).flatMap(apiInputPeer)
|
||||
}
|
||||
|
|
@ -112,7 +118,7 @@ public final class SparseMessageList {
|
|||
guard let messageFilter = messageFilterForTagMask(messageTag) else {
|
||||
return .single(SparseItems(items: []))
|
||||
}
|
||||
//TODO:api
|
||||
|
||||
return account.network.request(Api.functions.messages.getSearchResultsPositions(flags: 0, peer: inputPeer, savedPeerId: nil, filter: messageFilter, offsetId: 0, limit: 1000))
|
||||
|> map { result -> SparseItems in
|
||||
switch result {
|
||||
|
|
@ -136,7 +142,11 @@ public final class SparseMessageList {
|
|||
|
||||
var result = SparseItems(items: [])
|
||||
for i in 0 ..< positions.count {
|
||||
if i != 0 {
|
||||
if i == 0 {
|
||||
if initialMessageIndex != nil && positions[i].offset != 0 {
|
||||
result.items.append(.range(count: positions[i].offset))
|
||||
}
|
||||
} else {
|
||||
let deltaCount = positions[i].offset - 1 - positions[i - 1].offset
|
||||
if deltaCount > 0 {
|
||||
result.items.append(.range(count: deltaCount))
|
||||
|
|
@ -162,9 +172,34 @@ public final class SparseMessageList {
|
|||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.isLoadingInitial = false
|
||||
strongSelf.sparseItems = sparseItems
|
||||
if strongSelf.topSection != nil {
|
||||
strongSelf.updateState()
|
||||
if let initialMessageIndex {
|
||||
var loadHoleAnchor: MessageId?
|
||||
loop: for item in sparseItems.items {
|
||||
switch item {
|
||||
case let .anchor(id, timestamp, _):
|
||||
let anchorIndex = MessageIndex(id: id, timestamp: timestamp)
|
||||
if anchorIndex <= initialMessageIndex {
|
||||
loadHoleAnchor = id
|
||||
break loop
|
||||
}
|
||||
case .range:
|
||||
break
|
||||
}
|
||||
}
|
||||
if let loadHoleAnchor {
|
||||
strongSelf.loadHole(anchor: loadHoleAnchor, direction: .around, completion: {
|
||||
})
|
||||
} else {
|
||||
if strongSelf.topSection != nil {
|
||||
strongSelf.updateState()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if strongSelf.topSection != nil {
|
||||
strongSelf.updateState()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -593,10 +628,22 @@ public final class SparseMessageList {
|
|||
if self.topSection != topSection {
|
||||
self.topSection = topSection
|
||||
}
|
||||
self.updateState()
|
||||
if self.loadingHole == nil && !self.isLoadingInitial {
|
||||
self.updateState()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateState() {
|
||||
if self.isLoadingInitial {
|
||||
self.statePromise.set(.single(SparseMessageList.State(
|
||||
items: [],
|
||||
totalCount: 0,
|
||||
isLoading: true
|
||||
)))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var items: [SparseMessageList.State.Item] = []
|
||||
var minMessageId: MessageId?
|
||||
if let topSection = self.topSection {
|
||||
|
|
@ -694,11 +741,11 @@ public final class SparseMessageList {
|
|||
}
|
||||
}
|
||||
|
||||
init(account: Account, peerId: PeerId, threadId: Int64?, messageTag: MessageTags) {
|
||||
init(account: Account, peerId: PeerId, threadId: Int64?, messageTag: MessageTags, initialMessageIndex: MessageIndex?) {
|
||||
self.queue = Queue()
|
||||
let queue = self.queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, account: account, peerId: peerId, threadId: threadId, messageTag: messageTag)
|
||||
return Impl(queue: queue, account: account, peerId: peerId, threadId: threadId, messageTag: messageTag, initialMessageIndex: initialMessageIndex)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ public extension TelegramEngine {
|
|||
return _internal_requestMessageActionUrlAuth(account: self.account, subject: subject)
|
||||
}
|
||||
|
||||
public func acceptMessageActionUrlAuth(subject: MessageActionUrlSubject, allowWriteAccess: Bool, sharePhoneNumber: Bool) -> Signal<MessageActionUrlAuthResult, NoError> {
|
||||
public func acceptMessageActionUrlAuth(subject: MessageActionUrlSubject, allowWriteAccess: Bool, sharePhoneNumber: Bool) -> Signal<MessageActionUrlAuthResult, MessageActionUrlAuthError> {
|
||||
return _internal_acceptMessageActionUrlAuth(account: self.account, subject: subject, allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber)
|
||||
}
|
||||
|
||||
|
|
@ -495,8 +495,8 @@ public extension TelegramEngine {
|
|||
}
|
||||
}
|
||||
|
||||
public func sparseMessageList(peerId: EnginePeer.Id, threadId: Int64?, tag: EngineMessage.Tags) -> SparseMessageList {
|
||||
return SparseMessageList(account: self.account, peerId: peerId, threadId: threadId, messageTag: tag)
|
||||
public func sparseMessageList(peerId: EnginePeer.Id, threadId: Int64?, tag: EngineMessage.Tags, initialMessageIndex: MessageIndex? = nil) -> SparseMessageList {
|
||||
return SparseMessageList(account: self.account, peerId: peerId, threadId: threadId, messageTag: tag, initialMessageIndex: initialMessageIndex)
|
||||
}
|
||||
|
||||
public func sparseMessageCalendar(peerId: EnginePeer.Id, threadId: Int64?, tag: EngineMessage.Tags, displayMedia: Bool) -> SparseMessageCalendar {
|
||||
|
|
|
|||
|
|
@ -3595,33 +3595,36 @@ extension StarGift.UniqueGift.Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public enum GetUniqueStarGiftError {
|
||||
case generic
|
||||
case burned
|
||||
case invalidSlug
|
||||
case alreadyBurned
|
||||
}
|
||||
|
||||
func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal<StarGift.UniqueGift?, GetUniqueStarGiftError> {
|
||||
func _internal_getUniqueStarGift(account: Account, slug: String) -> Signal<StarGift.UniqueGift, GetUniqueStarGiftError> {
|
||||
return account.network.request(Api.functions.payments.getUniqueStarGift(slug: slug))
|
||||
|> mapError { error -> GetUniqueStarGiftError in
|
||||
if error.errorDescription == "STARGIFT_ALREADY_BURNED" {
|
||||
return .burned
|
||||
return .alreadyBurned
|
||||
} else if error.errorDescription == "STARGIFT_SLUG_INVALID" {
|
||||
return .invalidSlug
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { result -> Signal<StarGift.UniqueGift?, GetUniqueStarGiftError> in
|
||||
|> mapToSignal { result -> Signal<StarGift.UniqueGift, GetUniqueStarGiftError> in
|
||||
switch result {
|
||||
case let .uniqueStarGift(uniqueStarGiftData):
|
||||
let (gift, chats, users) = (uniqueStarGiftData.gift, uniqueStarGiftData.chats, uniqueStarGiftData.users)
|
||||
return account.postbox.transaction { transaction in
|
||||
return account.postbox.transaction { transaction -> Signal<StarGift.UniqueGift, GetUniqueStarGiftError> in
|
||||
let parsedPeers = AccumulatedPeers(chats: chats, users: users)
|
||||
updatePeers(transaction: transaction, accountPeerId: account.peerId, peers: parsedPeers)
|
||||
guard case let .unique(uniqueGift) = StarGift(apiStarGift: gift) else {
|
||||
return nil
|
||||
return .fail(.invalidSlug)
|
||||
}
|
||||
return uniqueGift
|
||||
return .single(uniqueGift)
|
||||
}
|
||||
|> castError(GetUniqueStarGiftError.self)
|
||||
|> switchToLatest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ public extension TelegramEngine {
|
|||
return _internal_checkCanSendStarGift(account: self.account, giftId: giftId)
|
||||
}
|
||||
|
||||
public func getUniqueStarGift(slug: String) -> Signal<StarGift.UniqueGift?, GetUniqueStarGiftError> {
|
||||
public func getUniqueStarGift(slug: String) -> Signal<StarGift.UniqueGift, GetUniqueStarGiftError> {
|
||||
return _internal_getUniqueStarGift(account: self.account, slug: slug)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1743,6 +1743,17 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGiftOffer_Rejected(peerName, giftTitle, priceString)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
}
|
||||
case let .groupCreatorChange(groupCreatorChange):
|
||||
var targetName = ""
|
||||
if let peer = message.peers[groupCreatorChange.targetPeerId] {
|
||||
targetName = EnginePeer(peer).displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
switch groupCreatorChange.kind {
|
||||
case .pending:
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_GroupCreatorChangePending(authorName, targetName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (1, groupCreatorChange.targetPeerId)]))
|
||||
case .applied:
|
||||
attributedString = addAttributesToStringWithRanges(strings.Notification_GroupCreatorChangeApplied(authorName, targetName)._tuple, body: bodyAttributes, argumentAttributes: peerMentionsAttributes(primaryTextColor: primaryTextColor, peerIds: [(0, message.author?.id), (1, groupCreatorChange.targetPeerId)]))
|
||||
}
|
||||
case .unknown:
|
||||
attributedString = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/AvatarComponent",
|
||||
"//submodules/Markdown",
|
||||
"//submodules/PhoneNumberFormat",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/AccountUtils",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/AccountPeerContextItem",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import GlassBackgroundComponent
|
||||
import AvatarComponent
|
||||
import BundleIconComponent
|
||||
import AccountContext
|
||||
|
||||
final class AccountSwitchComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let peer: EnginePeer
|
||||
let canSwitch: Bool
|
||||
let action: ((GlassContextExtractableContainer) -> Void)
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
peer: EnginePeer,
|
||||
canSwitch: Bool,
|
||||
action: @escaping ((GlassContextExtractableContainer) -> Void)
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.peer = peer
|
||||
self.canSwitch = canSwitch
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: AccountSwitchComponent, rhs: AccountSwitchComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
if lhs.canSwitch != rhs.canSwitch {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView = GlassContextExtractableContainer()
|
||||
private let avatar = ComponentView<Empty>()
|
||||
private let arrow = ComponentView<Empty>()
|
||||
private let button = HighlightTrackingButton()
|
||||
|
||||
private var component: AccountSwitchComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
self.button.addTarget(self, action: #selector(self.buttonPressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
if let component = self.component {
|
||||
component.action(self.backgroundView)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: AccountSwitchComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
|
||||
let size = CGSize(width: component.canSwitch ? 76.0 : 44.0, height: 44.0)
|
||||
|
||||
let avatarSize = self.avatar.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
AvatarComponent(
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
peer: component.peer,
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 36.0, height: 36.0)
|
||||
)
|
||||
if let avatarView = self.avatar.view {
|
||||
if avatarView.superview == nil {
|
||||
avatarView.isUserInteractionEnabled = false
|
||||
self.backgroundView.contentView.addSubview(avatarView)
|
||||
}
|
||||
avatarView.frame = CGRect(origin: CGPoint(x: 4.0, y: 4.0), size: avatarSize)
|
||||
}
|
||||
|
||||
let arrowSize = self.arrow.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
BundleIconComponent(name: "Navigation/Disclosure", tintColor: component.theme.rootController.navigationBar.secondaryTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
if let arrowView = self.arrow.view {
|
||||
if arrowView.superview == nil {
|
||||
arrowView.isUserInteractionEnabled = false
|
||||
self.backgroundView.contentView.addSubview(arrowView)
|
||||
self.backgroundView.contentView.addSubview(self.button)
|
||||
}
|
||||
arrowView.frame = CGRect(origin: CGPoint(x: size.width - arrowSize.width - 12.0, y: floorToScreenPixels((size.height - arrowSize.height) / 2.0)), size: arrowSize)
|
||||
transition.setAlpha(view: arrowView, alpha: component.canSwitch ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
self.backgroundView.update(size: size, cornerRadius: size.height * 0.5, isDark: component.theme.overallDarkAppearance, tintColor: .init(kind: .panel), isInteractive: component.canSwitch, transition: transition)
|
||||
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: .zero, size: size))
|
||||
transition.setFrame(view: self.button, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
return size
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,10 @@ import ListActionItemComponent
|
|||
import AvatarComponent
|
||||
import Markdown
|
||||
import PhoneNumberFormat
|
||||
import ContextUI
|
||||
import AccountUtils
|
||||
import GlassBackgroundComponent
|
||||
import AccountPeerContextItem
|
||||
|
||||
private final class AuthConfirmationSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -52,6 +56,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
private let subject: MessageActionUrlAuthResult
|
||||
|
||||
var peer: EnginePeer?
|
||||
var forcedAccount: (AccountContext, EnginePeer)?
|
||||
|
||||
fileprivate var inProgress = false
|
||||
var allowWrite = true
|
||||
|
|
@ -77,24 +82,24 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
guard case let .request(domain, _, _, _) = self.subject else {
|
||||
return
|
||||
}
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: self.context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, case let .user(user) = peer, let phone = user.phone else {
|
||||
return
|
||||
}
|
||||
let phoneNumber = formatPhoneNumber(context: self.context, number: phone)
|
||||
let phoneNumber = formatPhoneNumber(context: self.context, number: phone).replacingOccurrences(of: " ", with: "\u{00A0}")
|
||||
|
||||
//TODO:localize
|
||||
let alertController = textAlertController(
|
||||
context: self.context,
|
||||
title: "Phone Number",
|
||||
text: "**\(domain)** wants to access your phone number **\(phoneNumber)**.\n\nAllow access?",
|
||||
title: presentationData.strings.AuthConfirmation_PhoneNumberConfirmation_Title,
|
||||
text: presentationData.strings.AuthConfirmation_PhoneNumberConfirmation_Text(domain, phoneNumber).string,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Deny", action: {
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.AuthConfirmation_PhoneNumberConfirmation_Deny, action: {
|
||||
commit(false)
|
||||
}),
|
||||
TextAlertAction(type: .defaultAction, title: "Allow", action: {
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.AuthConfirmation_PhoneNumberConfirmation_Allow, action: {
|
||||
commit(true)
|
||||
})
|
||||
]
|
||||
|
|
@ -102,6 +107,51 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
self.controller?.present(alertController, in: .window(.root))
|
||||
})
|
||||
}
|
||||
|
||||
func presentAccountSwitchMenu(sourceView: GlassContextExtractableContainer) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let items: Signal<[ContextMenuItem], NoError> = activeAccountsAndPeers(context: self.context, includePrimary: true)
|
||||
|> take(1)
|
||||
|> map { primary, other -> [ContextMenuItem] in
|
||||
var items: [ContextMenuItem] = []
|
||||
var existingIds = Set<EnginePeer.Id>()
|
||||
if let (_, peer) = primary {
|
||||
items.append(.custom(AccountPeerContextItem(context: context, account: context.account, peer: peer, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.forcedAccount = nil
|
||||
self.updated()
|
||||
}), true))
|
||||
existingIds.insert(peer.id)
|
||||
}
|
||||
|
||||
for (accountContext, peer, _) in other {
|
||||
guard !existingIds.contains(peer.id) else {
|
||||
continue
|
||||
}
|
||||
items.append(.custom(AccountPeerContextItem(context: accountContext, account: accountContext.account, peer: peer, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.forcedAccount = (accountContext, peer)
|
||||
self.updated()
|
||||
}), true))
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(AuthConfirmationReferenceContentSource(controller: controller, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
}
|
||||
|
||||
func makeState() -> State {
|
||||
|
|
@ -110,7 +160,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
|
||||
static var body: Body {
|
||||
let closeButton = Child(GlassBarButtonComponent.self)
|
||||
let peerButton = Child(AvatarComponent.self)
|
||||
let accountButton = Child(AccountSwitchComponent.self)
|
||||
let avatar = Child(AvatarComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let description = Child(MultilineTextComponent.self)
|
||||
|
|
@ -162,17 +212,21 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
)
|
||||
|
||||
if let peer = state.peer {
|
||||
let peerButton = peerButton.update(
|
||||
component: AvatarComponent(
|
||||
context: component.context,
|
||||
let accountButton = accountButton.update(
|
||||
component: AccountSwitchComponent(
|
||||
context: state.forcedAccount?.0 ?? component.context,
|
||||
theme: environment.theme,
|
||||
peer: peer
|
||||
peer: state.forcedAccount?.1 ?? peer,
|
||||
canSwitch: true,
|
||||
action: { [weak state] sourceView in
|
||||
state?.presentAccountSwitchMenu(sourceView: sourceView)
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
availableSize: context.availableSize,
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(peerButton
|
||||
.position(CGPoint(x: context.availableSize.width - 16.0 - peerButton.size.width / 2.0, y: 16.0 + peerButton.size.height / 2.0))
|
||||
context.add(accountButton
|
||||
.position(CGPoint(x: context.availableSize.width - 16.0 - accountButton.size.width / 2.0, y: 16.0 + accountButton.size.height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -192,11 +246,11 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
)
|
||||
contentHeight += avatar.size.height
|
||||
contentHeight += 18.0
|
||||
|
||||
|
||||
let titleFont = Font.bold(24.0)
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .markdown(text: "Log in to **\(domain)**", attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.controlAccentColor), link: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
text: .markdown(text: strings.AuthConfirmation_Title(domain).string, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.controlAccentColor), link: MarkdownAttributeSet(font: titleFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 2
|
||||
),
|
||||
|
|
@ -213,7 +267,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
let boldTextFont = Font.semibold(15.0)
|
||||
let description = description.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .markdown(text: "This site will receive your **name**,\n**username** and **profile photo**.", attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.actionSheet.primaryTextColor), link: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
text: .markdown(text: strings.AuthConfirmation_Description, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: theme.actionSheet.primaryTextColor), link: MarkdownAttributeSet(font: textFont, textColor: theme.actionSheet.primaryTextColor), linkAttribute: { _ in return nil })),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 3,
|
||||
lineSpacing: 0.2
|
||||
|
|
@ -235,7 +289,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
style: .glass,
|
||||
title: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Device",
|
||||
string: strings.AuthConfirmation_Device,
|
||||
font: Font.regular(17.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
|
|
@ -283,7 +337,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
style: .glass,
|
||||
title: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "IP Address",
|
||||
string: strings.AuthConfirmation_IpAddress,
|
||||
font: Font.regular(17.0),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
|
|
@ -331,7 +385,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
header: nil,
|
||||
footer: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "This login attempt came from the device above.",
|
||||
string: strings.AuthConfirmation_Info,
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
textColor: environment.theme.list.freeTextColor
|
||||
)),
|
||||
|
|
@ -357,7 +411,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
title: AnyComponent(VStack([
|
||||
AnyComponentWithIdentity(id: AnyHashable(0), component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "Allow Messages",
|
||||
string: strings.AuthConfirmation_AllowMessages,
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseFontSize),
|
||||
textColor: theme.list.itemPrimaryTextColor
|
||||
)),
|
||||
|
|
@ -380,7 +434,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
header: nil,
|
||||
footer: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: "This will allow \(EnginePeer(bot).compactDisplayTitle) to message you.",
|
||||
string: strings.AuthConfirmation_AllowMessagesInfo(EnginePeer(bot).compactDisplayTitle).string,
|
||||
font: Font.regular(presentationData.listsFontSize.itemListBaseHeaderFontSize),
|
||||
textColor: environment.theme.list.freeTextColor
|
||||
)),
|
||||
|
|
@ -412,7 +466,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: "Cancel", font: Font.semibold(17.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center))))
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: strings.AuthConfirmation_Cancel, font: Font.semibold(17.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center))))
|
||||
),
|
||||
action: {
|
||||
component.cancel(true)
|
||||
|
|
@ -436,7 +490,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: "Log In", font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: strings.AuthConfirmation_LogIn, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
displaysProgress: state.inProgress,
|
||||
action: { [weak state] in
|
||||
|
|
@ -610,3 +664,19 @@ public class AuthConfirmationScreen: ViewControllerComponentContainer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AuthConfirmationReferenceContentSource: ContextReferenceContentSource {
|
||||
private let controller: ViewController
|
||||
private let sourceView: UIView
|
||||
|
||||
let forceDisplayBelowKeyboard = true
|
||||
|
||||
init(controller: ViewController, sourceView: UIView) {
|
||||
self.controller = controller
|
||||
self.sourceView = sourceView
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerReferenceViewInfo? {
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceView, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ swift_library(
|
|||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/TelegramUI/Components/ChatControllerInteraction",
|
||||
"//submodules/TelegramUI/Components/ChatInputNode",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -11,20 +11,19 @@ import ChatPresentationInterfaceState
|
|||
import WallpaperBackgroundNode
|
||||
import ChatControllerInteraction
|
||||
import ChatInputNode
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import GlassBackgroundComponent
|
||||
import EmojiStatusComponent
|
||||
|
||||
private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNode {
|
||||
var button: ReplyMarkupButton? {
|
||||
didSet {
|
||||
self.updateIcon()
|
||||
}
|
||||
}
|
||||
private(set) var button: ReplyMarkupButton?
|
||||
|
||||
private let backgroundContainerNode: ASDisplayNode
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let backgroundColorNode: ASDisplayNode
|
||||
private let backgroundAdditionalColorNode: ASDisplayNode
|
||||
private let backgroundView: UIImageView
|
||||
private var icon: ComponentView<Empty>?
|
||||
|
||||
private let highlightNode: ASImageNode
|
||||
let tintMaskView: UIImageView
|
||||
|
||||
private let textNode: ImmediateTextNode
|
||||
private var iconNode: ASImageNode?
|
||||
|
|
@ -33,22 +32,11 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo
|
|||
|
||||
init() {
|
||||
self.backgroundContainerNode = ASDisplayNode()
|
||||
self.backgroundContainerNode.clipsToBounds = true
|
||||
self.backgroundContainerNode.allowsGroupOpacity = true
|
||||
self.backgroundContainerNode.isUserInteractionEnabled = false
|
||||
self.backgroundContainerNode.cornerRadius = 10.0
|
||||
self.backgroundContainerNode.layer.cornerCurve = .continuous
|
||||
|
||||
self.backgroundColorNode = ASDisplayNode()
|
||||
self.backgroundColorNode.cornerRadius = 10.0
|
||||
self.backgroundColorNode.layer.cornerCurve = .continuous
|
||||
|
||||
self.backgroundAdditionalColorNode = ASDisplayNode()
|
||||
self.backgroundAdditionalColorNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.1)
|
||||
self.backgroundAdditionalColorNode.isHidden = true
|
||||
|
||||
self.highlightNode = ASImageNode()
|
||||
self.highlightNode.isUserInteractionEnabled = false
|
||||
self.backgroundView = UIImageView()
|
||||
self.tintMaskView = UIImageView()
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
|
|
@ -61,58 +49,68 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo
|
|||
|
||||
self.addSubnode(self.backgroundContainerNode)
|
||||
|
||||
self.backgroundContainerNode.addSubnode(self.backgroundColorNode)
|
||||
self.backgroundContainerNode.addSubnode(self.backgroundAdditionalColorNode)
|
||||
self.addSubnode(self.textNode)
|
||||
self.backgroundView.isUserInteractionEnabled = false
|
||||
self.backgroundContainerNode.view.addSubview(self.backgroundView)
|
||||
|
||||
self.backgroundContainerNode.addSubnode(self.highlightNode)
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted, !strongSelf.bounds.width.isZero {
|
||||
let scale = (strongSelf.bounds.width - 10.0) / strongSelf.bounds.width
|
||||
strongSelf.layer.animateScale(from: 1.0, to: scale, duration: 0.15, removeOnCompletion: false)
|
||||
|
||||
strongSelf.backgroundContainerNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.backgroundContainerNode.alpha = 0.6
|
||||
} else if let presentationLayer = strongSelf.layer.presentation() {
|
||||
strongSelf.layer.animateScale(from: CGFloat((presentationLayer.value(forKeyPath: "transform.scale.y") as? NSNumber)?.floatValue ?? 1.0), to: 1.0, duration: 0.25, removeOnCompletion: false)
|
||||
|
||||
strongSelf.backgroundContainerNode.alpha = 0.9
|
||||
} else {
|
||||
strongSelf.backgroundContainerNode.alpha = 1.0
|
||||
strongSelf.backgroundContainerNode.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
|
||||
strongSelf.backgroundContainerNode.layer.animateAlpha(from: 0.9, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func setAttributedTitle(_ title: NSAttributedString, for state: UIControl.State) {
|
||||
func update(context: AccountContext, size: CGSize, theme: PresentationTheme, wallpaperBackgroundNode: WallpaperBackgroundNode?, button: ReplyMarkupButton, message: EngineMessage) {
|
||||
self.button = button
|
||||
|
||||
if theme !== self.theme {
|
||||
self.theme = theme
|
||||
}
|
||||
|
||||
if self.backgroundView.image == nil {
|
||||
self.backgroundView.image = generateStretchableFilledCircleImage(diameter: 20.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.tintMaskView.image = self.backgroundView.image
|
||||
}
|
||||
self.tintMaskView.tintColor = .black
|
||||
|
||||
var titleColor = theme.chat.inputButtonPanel.buttonTextColor
|
||||
|
||||
if let color = button.style?.color {
|
||||
switch color {
|
||||
case .primary:
|
||||
self.backgroundView.tintColor = theme.list.itemCheckColors.fillColor
|
||||
titleColor = theme.list.itemCheckColors.foregroundColor
|
||||
case .danger:
|
||||
self.backgroundView.tintColor = UIColor(rgb: 0xFF3B30)
|
||||
titleColor = .white
|
||||
case .success:
|
||||
self.backgroundView.tintColor = UIColor(rgb: 0x21B246)
|
||||
titleColor = .white
|
||||
}
|
||||
} else {
|
||||
self.backgroundView.tintColor = theme.overallDarkAppearance ? UIColor(white: 1.0, alpha: 0.25) : UIColor(white: 1.0, alpha: 0.85)
|
||||
}
|
||||
|
||||
let title = NSAttributedString(string: button.title, font: Font.regular(16.0), textColor: titleColor, paragraphAlignment: .center)
|
||||
|
||||
self.textNode.attributedText = title
|
||||
self.accessibilityLabel = title.string
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
func update(rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
var backgroundFrame = backgroundNode.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateIcon() {
|
||||
guard let theme = self.theme else {
|
||||
return
|
||||
}
|
||||
var iconImage: UIImage?
|
||||
if let button = self.button {
|
||||
switch button.action {
|
||||
case .openWebView:
|
||||
iconImage = PresentationResourcesChat.chatKeyboardActionButtonWebAppIconImage(theme)
|
||||
default:
|
||||
iconImage = nil
|
||||
case .openWebView:
|
||||
iconImage = PresentationResourcesChat.chatKeyboardActionButtonWebAppIconImage(theme)
|
||||
default:
|
||||
iconImage = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +119,7 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo
|
|||
let iconNode = ASImageNode()
|
||||
iconNode.contentMode = .center
|
||||
self.iconNode = iconNode
|
||||
iconNode.isUserInteractionEnabled = false
|
||||
self.addSubnode(iconNode)
|
||||
}
|
||||
self.iconNode?.image = iconImage
|
||||
|
|
@ -129,71 +128,84 @@ private final class ChatButtonKeyboardInputButtonNode: HighlightTrackingButtonNo
|
|||
self.iconNode = nil
|
||||
}
|
||||
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
|
||||
func updateTheme(theme: PresentationTheme, wallpaperBackgroundNode: WallpaperBackgroundNode?) {
|
||||
if theme !== self.theme {
|
||||
self.theme = theme
|
||||
|
||||
self.highlightNode.image = PresentationResourcesChat.chatInputButtonPanelButtonHighlightImage(theme)
|
||||
|
||||
self.updateIcon()
|
||||
}
|
||||
|
||||
self.backgroundColorNode.backgroundColor = theme.chat.inputButtonPanel.buttonFillColor
|
||||
if let alpha = self.backgroundColorNode.backgroundColor?.alpha, alpha < 1.0 {
|
||||
self.backgroundColorNode.layer.compositingFilter = "softLightBlendMode"
|
||||
self.backgroundAdditionalColorNode.isHidden = false
|
||||
} else {
|
||||
self.backgroundColorNode.layer.compositingFilter = nil
|
||||
self.backgroundAdditionalColorNode.isHidden = true
|
||||
}
|
||||
|
||||
if wallpaperBackgroundNode?.hasExtraBubbleBackground() == true {
|
||||
if self.backgroundNode == nil, let backgroundContent = wallpaperBackgroundNode?.makeBubbleBackground(for: .free) {
|
||||
self.backgroundNode = backgroundContent
|
||||
self.backgroundContainerNode.insertSubnode(backgroundContent, at: 0)
|
||||
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
} else {
|
||||
self.backgroundNode?.removeFromSupernode()
|
||||
self.backgroundNode = nil
|
||||
}
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.backgroundContainerNode.frame = self.bounds
|
||||
self.backgroundColorNode.frame = CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: self.bounds.height - 1.0))
|
||||
self.backgroundAdditionalColorNode.frame = self.backgroundColorNode.frame
|
||||
self.backgroundNode?.frame = self.backgroundColorNode.frame
|
||||
|
||||
self.highlightNode.frame = self.bounds
|
||||
|
||||
if let (rect, containerSize) = self.absoluteRect {
|
||||
self.update(rect: rect, within: containerSize, transition: .immediate)
|
||||
}
|
||||
self.backgroundView.frame = CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: self.bounds.height))
|
||||
|
||||
if let iconNode = self.iconNode {
|
||||
iconNode.frame = CGRect(x: self.frame.width - 16.0, y: 4.0, width: 12.0, height: 12.0)
|
||||
}
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: self.bounds.width - 16.0, height: self.bounds.height))
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - textSize.width) / 2.0), y: floorToScreenPixels((self.bounds.height - textSize.height) / 2.0)), size: textSize)
|
||||
var maxTextWidth = size.width - 16.0
|
||||
let iconSize = CGSize(width: 24.0, height: 24.0)
|
||||
let iconSpacing: CGFloat = 6.0
|
||||
if let iconFileId = button.style?.iconFileId {
|
||||
let icon: ComponentView<Empty>
|
||||
if let current = self.icon {
|
||||
icon = current
|
||||
} else {
|
||||
icon = ComponentView()
|
||||
self.icon = icon
|
||||
}
|
||||
maxTextWidth -= iconSize.width + iconSpacing
|
||||
|
||||
var animationContent: EmojiStatusComponent.AnimationContent = .customEmoji(fileId: iconFileId)
|
||||
if let file = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: iconFileId)] as? TelegramMediaFile {
|
||||
animationContent = .file(file: file)
|
||||
}
|
||||
|
||||
let _ = icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: context,
|
||||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer,
|
||||
content: .animation(
|
||||
content: animationContent,
|
||||
size: iconSize,
|
||||
placeholderColor: theme.overallDarkAppearance ? UIColor(white: 1.0, alpha: 0.1) : UIColor(white: 0.0, alpha: 0.1),
|
||||
themeColor: theme.list.itemPrimaryTextColor,
|
||||
loopMode: .count(0)
|
||||
),
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: iconSize
|
||||
)
|
||||
} else if let icon = self.icon {
|
||||
self.icon = nil
|
||||
icon.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: maxTextWidth, height: self.bounds.height))
|
||||
|
||||
var textFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((self.bounds.width - textSize.width) / 2.0), y: floorToScreenPixels((self.bounds.height - textSize.height) / 2.0)), size: textSize)
|
||||
if let iconView = self.icon?.view {
|
||||
let contentX = floor((size.width - textSize.width - iconSize.width - iconSpacing) * 0.5)
|
||||
textFrame.origin.x = contentX + iconSize.width + iconSpacing
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: contentX, y: floor((size.height - iconSize.height) * 0.5)), size: iconSize)
|
||||
if iconView.superview == nil {
|
||||
iconView.isUserInteractionEnabled = false
|
||||
self.view.addSubview(iconView)
|
||||
}
|
||||
iconView.frame = iconFrame
|
||||
}
|
||||
|
||||
self.textNode.frame = textFrame
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
public final class ChatButtonKeyboardInputNode: ChatInputNode, UIScrollViewDelegate {
|
||||
private let context: AccountContext
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
private let backgroundView: BlurredBackgroundView
|
||||
private let backgroundTintView: UIImageView
|
||||
private let backgroundTintMaskView: UIView
|
||||
private let backgroundChromeView: UIImageView
|
||||
|
||||
private let scrollNode: ASScrollNode
|
||||
|
||||
private var backgroundNode: WallpaperBubbleBackgroundNode?
|
||||
private let backgroundColorNode: ASDisplayNode
|
||||
|
||||
private var buttonNodes: [ChatButtonKeyboardInputButtonNode] = []
|
||||
private var message: Message?
|
||||
|
|
@ -204,19 +216,42 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||
self.context = context
|
||||
self.controllerInteraction = controllerInteraction
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.backgroundView = BlurredBackgroundView(color: .black, enableBlur: true)
|
||||
self.backgroundTintView = UIImageView()
|
||||
|
||||
self.backgroundColorNode = ASDisplayNode()
|
||||
self.backgroundTintMaskView = UIView()
|
||||
self.backgroundTintMaskView.backgroundColor = .white
|
||||
|
||||
self.backgroundChromeView = UIImageView()
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.backgroundColorNode)
|
||||
self.view.addSubview(self.backgroundView)
|
||||
|
||||
self.view.addSubview(self.backgroundTintView)
|
||||
if let filter = CALayer.luminanceToAlpha() {
|
||||
self.backgroundTintMaskView.layer.filters = [filter]
|
||||
self.backgroundTintView.mask = self.backgroundTintMaskView
|
||||
}
|
||||
|
||||
self.view.addSubview(self.backgroundChromeView)
|
||||
|
||||
self.addSubnode(self.scrollNode)
|
||||
self.scrollNode.view.delaysContentTouches = false
|
||||
self.scrollNode.view.canCancelContentTouches = true
|
||||
self.scrollNode.view.alwaysBounceHorizontal = false
|
||||
self.scrollNode.view.alwaysBounceVertical = false
|
||||
self.scrollNode.view.delegate = self
|
||||
|
||||
self.scrollNode.view.clipsToBounds = true
|
||||
self.scrollNode.cornerRadius = 30.0
|
||||
self.scrollNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.backgroundTintMaskView.clipsToBounds = true
|
||||
self.backgroundTintMaskView.layer.cornerRadius = 30.0
|
||||
self.backgroundTintMaskView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
}
|
||||
|
||||
override public func didLoad() {
|
||||
|
|
@ -227,38 +262,16 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||
}
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize, transition: ContainedViewLayoutTransition) {
|
||||
self.absoluteRect = (rect, containerSize)
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
var backgroundFrame = backgroundNode.frame
|
||||
backgroundFrame.origin.x += rect.minX
|
||||
backgroundFrame.origin.y += rect.minY
|
||||
backgroundNode.update(rect: backgroundFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
|
||||
for buttonNode in self.buttonNodes {
|
||||
var buttonFrame = buttonNode.frame
|
||||
buttonFrame.origin.x += rect.minX
|
||||
buttonFrame.origin.y += rect.minY
|
||||
buttonNode.update(rect: buttonFrame, within: containerSize, transition: transition)
|
||||
}
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
var bounds = self.backgroundTintMaskView.bounds
|
||||
bounds.origin.y = scrollView.contentOffset.y
|
||||
self.backgroundTintMaskView.bounds = bounds
|
||||
}
|
||||
|
||||
override public func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, standardInputHeight: CGFloat, inputHeight: CGFloat, maximumHeight: CGFloat, inputPanelHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, layoutMetrics: LayoutMetrics, deviceMetrics: DeviceMetrics, isVisible: Bool, isExpanded: Bool) -> (CGFloat, CGFloat) {
|
||||
if self.backgroundNode == nil {
|
||||
if let backgroundNode = self.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
self.backgroundNode = backgroundNode
|
||||
self.insertSubnode(backgroundNode, at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
let updatedTheme = self.theme !== interfaceState.theme
|
||||
if updatedTheme {
|
||||
self.theme = interfaceState.theme
|
||||
|
||||
self.backgroundColorNode.backgroundColor = interfaceState.theme.chat.inputButtonPanel.panelBackgroundColor
|
||||
}
|
||||
|
||||
var validatedMarkup: ReplyMarkupMessageAttribute?
|
||||
|
|
@ -275,9 +288,9 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||
|
||||
self.message = interfaceState.keyboardButtonsMessage
|
||||
|
||||
if let markup = validatedMarkup {
|
||||
let verticalInset: CGFloat = 16.0
|
||||
let sideInset: CGFloat = 16.0 + leftInset
|
||||
if let markup = validatedMarkup, let message = self.message {
|
||||
let verticalInset: CGFloat = 18.0
|
||||
let sideInset: CGFloat = 18.0 + leftInset
|
||||
var buttonHeight: CGFloat = 43.0
|
||||
let columnSpacing: CGFloat = 6.0
|
||||
let rowSpacing: CGFloat = 5.0
|
||||
|
|
@ -305,15 +318,14 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||
buttonNode.titleNode.maximumNumberOfLines = 2
|
||||
buttonNode.addTarget(self, action: #selector(self.buttonPressed(_:)), forControlEvents: [.touchUpInside])
|
||||
self.scrollNode.addSubnode(buttonNode)
|
||||
self.backgroundTintMaskView.addSubview(buttonNode.tintMaskView)
|
||||
self.buttonNodes.append(buttonNode)
|
||||
}
|
||||
buttonNode.updateTheme(theme: interfaceState.theme, wallpaperBackgroundNode: self.controllerInteraction.presentationContext.backgroundNode)
|
||||
buttonIndex += 1
|
||||
if buttonNode.button != button || updatedTheme {
|
||||
buttonNode.button = button
|
||||
buttonNode.setAttributedTitle(NSAttributedString(string: button.title, font: Font.regular(16.0), textColor: interfaceState.theme.chat.inputButtonPanel.buttonTextColor, paragraphAlignment: .center), for: [])
|
||||
}
|
||||
buttonNode.frame = CGRect(origin: CGPoint(x: sideInset + CGFloat(columnIndex) * (buttonWidth + columnSpacing), y: verticalOffset), size: CGSize(width: buttonWidth, height: buttonHeight))
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset + CGFloat(columnIndex) * (buttonWidth + columnSpacing), y: verticalOffset), size: CGSize(width: buttonWidth, height: buttonHeight))
|
||||
buttonNode.frame = buttonFrame
|
||||
buttonNode.tintMaskView.frame = buttonFrame
|
||||
buttonNode.update(context: self.context, size: buttonFrame.size, theme: interfaceState.theme, wallpaperBackgroundNode: self.controllerInteraction.presentationContext.backgroundNode, button: button, message: EngineMessage(message))
|
||||
columnIndex += 1
|
||||
}
|
||||
verticalOffset += buttonHeight + rowSpacing
|
||||
|
|
@ -321,6 +333,7 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||
|
||||
for i in (buttonIndex ..< self.buttonNodes.count).reversed() {
|
||||
self.buttonNodes[i].removeFromSupernode()
|
||||
self.buttonNodes[i].tintMaskView.removeFromSuperview()
|
||||
self.buttonNodes.remove(at: i)
|
||||
}
|
||||
|
||||
|
|
@ -335,15 +348,28 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode {
|
|||
self.scrollNode.view.setContentOffset(CGPoint(), animated: false)
|
||||
}
|
||||
|
||||
if let backgroundNode = self.backgroundNode {
|
||||
backgroundNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||
}
|
||||
self.backgroundColorNode.frame = CGRect(origin: .zero, size: CGSize(width: width, height: panelHeight))
|
||||
var backgroundFrame = CGRect(origin: CGPoint(), size: CGSize(width: width, height: panelHeight))
|
||||
backgroundFrame.size.height += 32.0
|
||||
let keyboardCornerRadius: CGFloat = 30.0
|
||||
|
||||
if let (rect, containerSize) = self.absoluteRect {
|
||||
self.updateAbsoluteRect(rect, within: containerSize, transition: transition)
|
||||
if self.backgroundChromeView.image == nil || updatedTheme {
|
||||
self.backgroundChromeView.image = GlassBackgroundView.generateForegroundImage(size: CGSize(width: keyboardCornerRadius * 2.0, height: keyboardCornerRadius * 2.0), isDark: interfaceState.theme.overallDarkAppearance, fillColor: .clear)
|
||||
}
|
||||
|
||||
if self.backgroundTintView.image == nil {
|
||||
self.backgroundTintView.image = generateStretchableFilledCircleImage(diameter: keyboardCornerRadius * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
self.backgroundTintView.tintColor = interfaceState.theme.chat.inputButtonPanel.panelBackgroundColor
|
||||
|
||||
transition.updateFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
transition.updateFrame(view: self.backgroundTintView, frame: backgroundFrame)
|
||||
transition.updateFrame(view: self.backgroundTintMaskView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
|
||||
|
||||
self.backgroundView.updateColor(color: .clear, forceKeepBlur: true, transition: .immediate)
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: keyboardCornerRadius, maskedCorners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], transition: transition)
|
||||
|
||||
transition.updateFrame(view: self.backgroundChromeView, frame: backgroundFrame.insetBy(dx: -1.0, dy: 0.0))
|
||||
|
||||
return (panelHeight, 0.0)
|
||||
} else {
|
||||
return (0.0, 0.0)
|
||||
|
|
|
|||
|
|
@ -477,6 +477,7 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||
transition: ComponentTransition(transition),
|
||||
component: AnyComponent(GlassControlPanelComponent(
|
||||
theme: interfaceState.theme,
|
||||
preferClearGlass: interfaceState.preferredGlassType == .clear,
|
||||
leftItem: leftPanelItems.isEmpty ? nil : GlassControlPanelComponent.Item(
|
||||
items: leftPanelItems,
|
||||
background: .panel
|
||||
|
|
@ -498,118 +499,6 @@ public final class ChatChannelSubscriberInputPanelNode: ChatInputPanelNode {
|
|||
transition.updateFrame(view: panelView, frame: CGRect(origin: CGPoint(), size: panelFrame.size))
|
||||
}
|
||||
|
||||
/*if self.presentationInterfaceState != interfaceState || force {
|
||||
let previousState = self.presentationInterfaceState
|
||||
self.presentationInterfaceState = interfaceState
|
||||
|
||||
if previousState?.theme !== interfaceState.theme {
|
||||
self.helpButtonIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Help"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.helpButtonIconView.tintColor = interfaceState.theme.chat.inputPanel.panelControlColor
|
||||
|
||||
self.suggestedPostButtonIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/SuggestPost"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.suggestedPostButtonIconView.tintColor = interfaceState.theme.chat.inputPanel.panelControlColor
|
||||
|
||||
self.giftButtonIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/Gift"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.giftButtonIconView.tintColor = interfaceState.theme.chat.inputPanel.panelControlColor
|
||||
}
|
||||
|
||||
if let context = self.context, let peer = interfaceState.renderedPeer?.peer, previousState?.renderedPeer?.peer == nil || !peer.isEqual(previousState!.renderedPeer!.peer!) || previousState?.theme !== interfaceState.theme || previousState?.strings !== interfaceState.strings || previousState?.peerIsMuted != interfaceState.peerIsMuted || previousState?.pinnedMessage != interfaceState.pinnedMessage || force {
|
||||
|
||||
if let action = actionForPeer(context: context, peer: peer, interfaceState: interfaceState, isJoining: self.isJoining, isMuted: interfaceState.peerIsMuted) {
|
||||
let previousAction = self.action
|
||||
self.action = action
|
||||
let (title, _) = titleAndColorForAction(action, theme: interfaceState.theme, strings: interfaceState.strings)
|
||||
|
||||
let _ = previousAction
|
||||
|
||||
let titleColor: UIColor
|
||||
if case .join = self.action {
|
||||
titleColor = interfaceState.theme.chat.inputPanel.actionControlForegroundColor
|
||||
} else {
|
||||
titleColor = interfaceState.theme.chat.inputPanel.panelControlColor
|
||||
}
|
||||
self.buttonTitle.attributedText = NSAttributedString(string: title, font: Font.semibold(15.0), textColor: titleColor)
|
||||
self.buttonTintTitle.attributedText = NSAttributedString(string: title, font: Font.semibold(15.0), textColor: .black)
|
||||
self.button.accessibilityLabel = title
|
||||
} else {
|
||||
self.action = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let panelHeight = defaultHeight(metrics: metrics)
|
||||
|
||||
if let peer = interfaceState.renderedPeer?.peer as? TelegramChannel {
|
||||
if case let .broadcast(broadcastInfo) = peer.info, interfaceState.starGiftsAvailable {
|
||||
if self.giftButton.isHidden && !isFirstTime {
|
||||
self.giftButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
self.giftButton.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
self.giftButtonBackgroundView.isHidden = false
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = !broadcastInfo.flags.contains(.hasMonoforum)
|
||||
self.presentGiftOrSuggestTooltip()
|
||||
} else if case let .broadcast(broadcastInfo) = peer.info, broadcastInfo.flags.contains(.hasMonoforum) {
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = false
|
||||
self.presentGiftOrSuggestTooltip()
|
||||
} else if peer.flags.contains(.isGigagroup), self.action == .muteNotifications || self.action == .unmuteNotifications {
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = false
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
} else {
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
}
|
||||
} else {
|
||||
self.giftButtonBackgroundView.isHidden = true
|
||||
self.helpButtonBackgroundView.isHidden = true
|
||||
self.suggestedPostButtonBackgroundView.isHidden = true
|
||||
}
|
||||
|
||||
let buttonTitleSize = self.buttonTitle.updateLayout(CGSize(width: width, height: panelHeight))
|
||||
let _ = self.buttonTintTitle.updateLayout(CGSize(width: width, height: panelHeight))
|
||||
let buttonSize = CGSize(width: buttonTitleSize.width + 16.0 * 2.0, height: 40.0)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: floor((width - buttonSize.width) / 2.0), y: floor((panelHeight - buttonSize.height) * 0.5)), size: buttonSize)
|
||||
transition.updateFrame(view: self.buttonBackgroundView, frame: buttonFrame)
|
||||
transition.updateFrame(view: self.button, frame: CGRect(origin: CGPoint(), size: buttonFrame.size))
|
||||
let buttonTintColor: GlassBackgroundView.TintColor
|
||||
if case .join = self.action {
|
||||
buttonTintColor = .init(kind: .custom, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7), innerColor: interfaceState.theme.chat.inputPanel.actionControlFillColor)
|
||||
} else {
|
||||
buttonTintColor = .init(kind: .panel, color: interfaceState.theme.chat.inputPanel.inputBackgroundColor.withMultipliedAlpha(0.7))
|
||||
}
|
||||
self.buttonBackgroundView.update(size: buttonFrame.size, cornerRadius: buttonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: buttonTintColor, isInteractive: true, transition: ComponentTransition(transition))
|
||||
self.buttonTitle.frame = CGRect(origin: CGPoint(x: floor((buttonFrame.width - buttonTitleSize.width) * 0.5), y: floor((buttonFrame.height - buttonTitleSize.height) * 0.5)), size: buttonTitleSize)
|
||||
self.buttonTintTitle.frame = self.buttonTitle.frame
|
||||
|
||||
let giftButtonFrame = CGRect(x: width - rightInset - 40.0 - 8.0, y: floor((panelHeight - 40.0) * 0.5), width: 40.0, height: 40.0)
|
||||
transition.updateFrame(view: self.giftButtonBackgroundView, frame: giftButtonFrame)
|
||||
if let image = self.giftButtonIconView.image {
|
||||
transition.updateFrame(view: self.giftButtonIconView, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: giftButtonFrame.size)))
|
||||
}
|
||||
transition.updateFrame(view: self.giftButton, frame: CGRect(origin: CGPoint(), size: giftButtonFrame.size))
|
||||
self.giftButtonBackgroundView.update(size: giftButtonFrame.size, cornerRadius: giftButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel), isInteractive: true, transition: ComponentTransition(transition))
|
||||
|
||||
let helpButtonFrame = CGRect(x: width - rightInset - 8.0 - 40.0, y: floor((panelHeight - 40.0) * 0.5), width: 40.0, height: 40.0)
|
||||
transition.updateFrame(view: self.helpButtonBackgroundView, frame: helpButtonFrame)
|
||||
if let image = self.helpButtonIconView.image {
|
||||
transition.updateFrame(view: self.helpButtonIconView, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: helpButtonFrame.size)))
|
||||
}
|
||||
transition.updateFrame(view: self.helpButton, frame: CGRect(origin: CGPoint(), size: helpButtonFrame.size))
|
||||
self.helpButtonBackgroundView.update(size: helpButtonFrame.size, cornerRadius: helpButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel), isInteractive: true, transition: ComponentTransition(transition))
|
||||
|
||||
let suggestedPostButtonFrame = CGRect(x: leftInset + 8.0, y: floor((panelHeight - 40.0) * 0.5), width: 40.0, height: 40.0)
|
||||
transition.updateFrame(view: self.suggestedPostButtonBackgroundView, frame: suggestedPostButtonFrame)
|
||||
if let image = self.suggestedPostButtonIconView.image {
|
||||
transition.updateFrame(view: self.suggestedPostButtonIconView, frame: image.size.centered(in: CGRect(origin: CGPoint(), size: suggestedPostButtonFrame.size)))
|
||||
}
|
||||
transition.updateFrame(view: self.suggestedPostButton, frame: CGRect(origin: CGPoint(), size: suggestedPostButtonFrame.size))
|
||||
self.suggestedPostButtonBackgroundView.update(size: suggestedPostButtonFrame.size, cornerRadius: suggestedPostButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: .init(kind: .panel), isInteractive: true, transition: ComponentTransition(transition))*/
|
||||
|
||||
return panelHeight
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ swift_library(
|
|||
"//submodules/WallpaperBackgroundNode",
|
||||
"//submodules/UrlHandling",
|
||||
"//submodules/TelegramUI/Components/TextLoadingEffect",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/Components/ComponentDisplayAdapters",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import WallpaperBackgroundNode
|
|||
import UrlHandling
|
||||
import SwiftSignalKit
|
||||
import TextLoadingEffect
|
||||
import ComponentFlow
|
||||
import ComponentDisplayAdapters
|
||||
import EmojiStatusComponent
|
||||
|
||||
private let titleFont = Font.medium(16.0)
|
||||
|
||||
|
|
@ -70,9 +73,12 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
private var iconNode: ASImageNode?
|
||||
private var buttonView: HighlightTrackingButton?
|
||||
|
||||
private var icon: ComponentView<Empty>?
|
||||
|
||||
private var wallpaperBackgroundNode: WallpaperBackgroundNode?
|
||||
private var backgroundContent: WallpaperBubbleBackgroundNode?
|
||||
private var backgroundColorNode: ASDisplayNode?
|
||||
private var backgroundColorView: UIImageView?
|
||||
|
||||
private var maskPath: CGPath?
|
||||
private var loadingEffectView: TextLoadingEffectView?
|
||||
|
|
@ -175,6 +181,8 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
self.view.insertSubview(loadingEffectView, belowSubview: iconNode.view)
|
||||
} else if let titleNode = self.titleNode, titleNode.view.superview != nil {
|
||||
self.view.insertSubview(loadingEffectView, belowSubview: titleNode.view)
|
||||
} else if let iconView = self.icon?.view, iconView.superview != nil {
|
||||
self.view.insertSubview(loadingEffectView, belowSubview: iconView)
|
||||
} else {
|
||||
self.view.addSubview(loadingEffectView)
|
||||
}
|
||||
|
|
@ -320,6 +328,12 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
customIconSpaceWidth = 3.0 + iconImage.size.width
|
||||
}
|
||||
|
||||
let emojiIconSize = CGSize(width: 24.0, height: 24.0)
|
||||
let emojiIconSpacing: CGFloat = 6.0
|
||||
if button.style?.iconFileId != nil {
|
||||
customIconSpaceWidth = emojiIconSpacing + emojiIconSize.width
|
||||
}
|
||||
|
||||
let (titleSize, titleApply) = titleLayout(TextNodeLayoutArguments(attributedString: attributedTitle, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: max(44.0, constrainedWidth - minimumSideInset - minimumSideInset - customIconSpaceWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .center, cutout: nil, insets: UIEdgeInsets(top: 1.0, left: 0.0, bottom: 1.0, right: 0.0)))
|
||||
let contentWidth = titleSize.size.width + sideInset + sideInset + customIconSpaceWidth
|
||||
|
||||
|
|
@ -345,8 +359,6 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
node.longTapRecognizer?.isEnabled = false
|
||||
}
|
||||
|
||||
//animation.animator.updateFrame(layer: node.backgroundBlurNode.layer, frame: CGRect(origin: CGPoint(), size: CGSize(width: max(0.0, width), height: 42.0)), completion: nil)
|
||||
|
||||
if node.backgroundBlurView == nil {
|
||||
if let backgroundBlurView = backgroundNode?.makeFreeBackground() {
|
||||
node.backgroundBlurView = backgroundBlurView
|
||||
|
|
@ -357,9 +369,6 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
animation.animator.updateFrame(layer: backgroundBlurView.view.layer, frame: CGRect(origin: CGPoint(), size: CGSize(width: max(0.0, width), height: 42.0)), completion: nil)
|
||||
}
|
||||
|
||||
/*node.backgroundBlurNode.update(size: node.backgroundBlurNode.bounds.size, cornerRadius: 0.0, animator: animation.animator)
|
||||
node.backgroundBlurNode.updateColor(color: selectDateFillStaticColor(theme: theme.theme, wallpaper: theme.wallpaper), enableBlur: context.sharedContext.energyUsageSettings.fullTranslucency && dateFillNeedsBlur(theme: theme.theme, wallpaper: theme.wallpaper), transition: .immediate)*/
|
||||
|
||||
if backgroundNode?.hasExtraBubbleBackground() == true {
|
||||
if node.backgroundContent == nil, let backgroundContent = backgroundNode?.makeBubbleBackground(for: .free) {
|
||||
backgroundContent.clipsToBounds = true
|
||||
|
|
@ -401,7 +410,6 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
node.backgroundBlurView?.view.isHidden = false
|
||||
}
|
||||
|
||||
|
||||
let rect = CGRect(origin: CGPoint(), size: CGSize(width: max(0.0, width), height: 42.0))
|
||||
let maskPath: CGPath?
|
||||
var needsMask = true
|
||||
|
|
@ -435,6 +443,40 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
node.layer.mask = nil
|
||||
}
|
||||
}
|
||||
|
||||
if let color = button.style?.color {
|
||||
let backgroundColorView: UIImageView
|
||||
if let current = node.backgroundColorView {
|
||||
backgroundColorView = current
|
||||
} else {
|
||||
backgroundColorView = UIImageView()
|
||||
node.backgroundColorView = backgroundColorView
|
||||
|
||||
if let iconNode = node.iconNode, iconNode.view.superview != nil {
|
||||
node.view.insertSubview(backgroundColorView, belowSubview: iconNode.view)
|
||||
} else if let titleNode = node.titleNode, titleNode.view.superview != nil {
|
||||
node.view.insertSubview(backgroundColorView, belowSubview: titleNode.view)
|
||||
} else if let iconView = node.icon?.view, iconView.superview != nil {
|
||||
node.view.insertSubview(backgroundColorView, belowSubview: iconView)
|
||||
} else {
|
||||
node.view.addSubview(backgroundColorView)
|
||||
}
|
||||
}
|
||||
|
||||
animation.animator.updateFrame(layer: backgroundColorView.layer, frame: CGRect(origin: CGPoint(), size: CGSize(width: max(0.0, width), height: 42.0)), completion: nil)
|
||||
|
||||
switch color {
|
||||
case .primary:
|
||||
backgroundColorView.backgroundColor = UIColor(rgb: 0x0067D7, alpha: 0.55)
|
||||
case .danger:
|
||||
backgroundColorView.backgroundColor = UIColor(rgb: 0xD5000C, alpha: 0.55)
|
||||
case .success:
|
||||
backgroundColorView.backgroundColor = UIColor(rgb: 0x127E2D, alpha: 0.55)
|
||||
}
|
||||
} else if let backgroundColorView = node.backgroundColorView {
|
||||
node.backgroundColorView = nil
|
||||
backgroundColorView.removeFromSuperview()
|
||||
}
|
||||
|
||||
if iconImage != nil {
|
||||
if node.iconNode == nil {
|
||||
|
|
@ -459,7 +501,10 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
}
|
||||
|
||||
var titleFrame = CGRect(origin: CGPoint(x: floor((width - titleSize.size.width) / 2.0), y: floor((42.0 - titleSize.size.height) / 2.0) + 1.0), size: titleSize.size)
|
||||
if let image = node.iconNode?.image, customInfo?.icon != nil {
|
||||
|
||||
if button.style?.iconFileId != nil {
|
||||
titleFrame.origin.x = floorToScreenPixels((width - titleSize.size.width - emojiIconSize.width - emojiIconSpacing) * 0.5) + emojiIconSize.width + emojiIconSpacing
|
||||
} else if let image = node.iconNode?.image, customInfo?.icon != nil {
|
||||
if customInfo?.icon == .actionArrow {
|
||||
titleFrame.origin.x = floorToScreenPixels((width - titleSize.size.width - image.size.width + 1.0) * 0.5) - 0.0
|
||||
} else {
|
||||
|
|
@ -486,6 +531,56 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
|
|||
animation.animator.updateFrame(layer: iconNode.layer, frame: iconFrame, completion: nil)
|
||||
}
|
||||
|
||||
if let iconFileId = button.style?.iconFileId {
|
||||
let icon: ComponentView<Empty>
|
||||
var iconTransition = animation.animator
|
||||
if let current = node.icon {
|
||||
icon = current
|
||||
} else {
|
||||
iconTransition = ListViewItemUpdateAnimation.None.animator
|
||||
icon = ComponentView()
|
||||
node.icon = icon
|
||||
}
|
||||
|
||||
var animationContent: EmojiStatusComponent.AnimationContent = .customEmoji(fileId: iconFileId)
|
||||
if let file = message.associatedMedia[MediaId(namespace: Namespaces.Media.CloudFile, id: iconFileId)] as? TelegramMediaFile {
|
||||
animationContent = .file(file: file)
|
||||
}
|
||||
|
||||
let _ = icon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: context,
|
||||
animationCache: context.animationCache,
|
||||
animationRenderer: context.animationRenderer,
|
||||
content: .animation(
|
||||
content: animationContent,
|
||||
size: emojiIconSize,
|
||||
placeholderColor: theme.theme.overallDarkAppearance ? UIColor(white: 1.0, alpha: 0.1) : UIColor(white: 0.0, alpha: 0.1),
|
||||
themeColor: titleColor,
|
||||
loopMode: .count(0)
|
||||
),
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: emojiIconSize
|
||||
)
|
||||
|
||||
let contentX = titleFrame.origin.x - emojiIconSize.width - emojiIconSpacing
|
||||
|
||||
let iconFrame = CGRect(origin: CGPoint(x: contentX, y: floor((42.0 - emojiIconSize.height) * 0.5) - 1.0), size: emojiIconSize)
|
||||
if let iconView = icon.view {
|
||||
if iconView.superview == nil {
|
||||
node.view.addSubview(iconView)
|
||||
}
|
||||
iconTransition.updateFrame(layer: iconView.layer, frame: iconFrame, completion: nil)
|
||||
}
|
||||
} else if let icon = node.icon {
|
||||
node.icon = nil
|
||||
icon.view?.removeFromSuperview()
|
||||
}
|
||||
|
||||
if let (rect, size) = node.absolutePosition {
|
||||
node.updateAbsoluteRect(rect, within: size)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1410,11 +1410,11 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionReject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionApprove, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionReject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline), style: ReplyMarkupButton.Style(color: .danger, iconFileId: nil)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionApprove, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove), style: ReplyMarkupButton.Style(color: .success, iconFileId: nil))
|
||||
]),
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionSuggestChanges, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionSuggestChanges, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges), style: nil)
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
|
|
|
|||
|
|
@ -2848,7 +2848,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(
|
||||
buttons: [ReplyMarkupButton(title: item.presentationData.strings.Channel_AdminLog_ShowMoreMessages(Int32(messages.count - 1)), titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: MemoryBuffer(data: Data())))]
|
||||
buttons: [ReplyMarkupButton(title: item.presentationData.strings.Channel_AdminLog_ShowMoreMessages(Int32(messages.count - 1)), titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: MemoryBuffer(data: Data())), style: nil)]
|
||||
)
|
||||
],
|
||||
flags: [],
|
||||
|
|
@ -2887,8 +2887,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_GiftPurchaseOffer_Reject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_GiftPurchaseOffer_Accept, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_GiftPurchaseOffer_Reject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline), style: ReplyMarkupButton.Style(color: .danger, iconFileId: nil)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_GiftPurchaseOffer_Accept, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove), style: ReplyMarkupButton.Style(color: .success, iconFileId: nil))
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
|
|
@ -2936,11 +2936,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionReject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionApprove, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionReject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline), style: ReplyMarkupButton.Style(color: .danger, iconFileId: nil)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionApprove, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove), style: ReplyMarkupButton.Style(color: .success, iconFileId: nil))
|
||||
]),
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionSuggestChanges, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionSuggestChanges, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges), style: nil)
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
|
|
@ -2974,7 +2974,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_MessageContinueLastThread, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: button))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_MessageContinueLastThread, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: button), style: nil)
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
|
|
|
|||
|
|
@ -725,11 +725,18 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
} else {
|
||||
ribbonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_Gift
|
||||
}
|
||||
|
||||
buttonTitle = isStoryEntity ? "" : item.presentationData.strings.Notification_StarGift_View
|
||||
modelTitle = item.presentationData.strings.Notification_StarGift_Model
|
||||
backdropTitle = item.presentationData.strings.Notification_StarGift_Backdrop
|
||||
symbolTitle = item.presentationData.strings.Notification_StarGift_Symbol
|
||||
|
||||
if uniqueGift.flags.contains(.isBurned) {
|
||||
ribbonTitle = item.presentationData.strings.Notification_StarGift_Burned
|
||||
customRibbonColors = [UIColor(rgb: 0xd9433a), UIColor(rgb: 0xff645b)]
|
||||
buttonTitle = ""
|
||||
}
|
||||
|
||||
for attribute in uniqueGift.attributes {
|
||||
switch attribute {
|
||||
case let .model(name, file, _, _):
|
||||
|
|
|
|||
|
|
@ -897,11 +897,11 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
|||
ReplyMarkupMessageAttribute(
|
||||
rows: [
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionReject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionApprove, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionReject, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonDecline), style: ReplyMarkupButton.Style(color: .danger, iconFileId: nil)),
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionApprove, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonApprove), style: ReplyMarkupButton.Style(color: .success, iconFileId: nil))
|
||||
]),
|
||||
ReplyMarkupRow(buttons: [
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionSuggestChanges, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges))
|
||||
ReplyMarkupButton(title: item.presentationData.strings.Chat_PostApproval_Message_ActionSuggestChanges, titleWhenForwarded: nil, action: .callback(requiresPassword: false, data: buttonSuggestChanges), style: nil)
|
||||
])
|
||||
],
|
||||
flags: [],
|
||||
|
|
|
|||
|
|
@ -1422,10 +1422,13 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
self.validLayout = (width, leftInset, rightInset, bottomInset, additionalSideInsets, maxHeight, maxOverlayHeight, metrics, isSecondary, isMediaInputExpanded)
|
||||
|
||||
let defaultGlassTintColor: GlassBackgroundView.TintColor
|
||||
let defaultGlassTintWithInnerColor: GlassBackgroundView.TintColor
|
||||
if case .clear = interfaceState.preferredGlassType {
|
||||
defaultGlassTintColor = .init(kind: .clear)
|
||||
defaultGlassTintWithInnerColor = .init(kind: .clear, innerColor: interfaceState.theme.list.itemCheckColors.fillColor)
|
||||
} else {
|
||||
defaultGlassTintColor = .init(kind: .panel)
|
||||
defaultGlassTintWithInnerColor = .init(kind: .panel, innerColor: interfaceState.theme.list.itemCheckColors.fillColor)
|
||||
}
|
||||
|
||||
var leftInset = leftInset
|
||||
|
|
@ -2372,7 +2375,7 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
let menuButtonFrame = CGRect(x: leftInset + 8.0, y: menuButtonOriginY, width: menuButtonExpanded ? menuButtonWidth : menuCollapsedButtonWidth, height: menuButtonHeight)
|
||||
transition.updateFrameAsPositionAndBounds(node: self.menuButton, frame: menuButtonFrame)
|
||||
transition.updateFrame(view: self.menuButtonBackgroundView, frame: CGRect(origin: CGPoint(), size: menuButtonFrame.size))
|
||||
self.menuButtonBackgroundView.update(size: menuButtonFrame.size, cornerRadius: menuButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: defaultGlassTintColor, transition: ComponentTransition(transition))
|
||||
self.menuButtonBackgroundView.update(size: menuButtonFrame.size, cornerRadius: menuButtonFrame.height * 0.5, isDark: interfaceState.theme.overallDarkAppearance, tintColor: defaultGlassTintWithInnerColor, transition: ComponentTransition(transition))
|
||||
transition.updateFrame(node: self.menuButtonClippingNode, frame: CGRect(origin: CGPoint(x: 19.0, y: 0.0), size: CGSize(width: menuButtonWidth - 19.0, height: menuButtonFrame.height)))
|
||||
var menuButtonTitleTransition = transition
|
||||
if buttonTitleUpdated {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
|
|||
private var activities: ChatTitleComponent.Activities?
|
||||
private var networkState: AccountNetworkState?
|
||||
|
||||
private var ignoreParentTransitionRequests: Bool = false
|
||||
public var requestUpdate: ((ContainedViewLayoutTransition) -> Void)?
|
||||
|
||||
public var tapAction: (() -> Void)?
|
||||
public var longTapAction: (() -> Void)?
|
||||
|
||||
|
|
@ -82,8 +84,10 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
|
|||
dateTimeFormat: PresentationDateTimeFormat,
|
||||
nameDisplayOrder: PresentationPersonNameOrder,
|
||||
content: ChatTitleContent,
|
||||
transition: ComponentTransition
|
||||
transition: ComponentTransition,
|
||||
ignoreParentTransitionRequests: Bool = false
|
||||
) {
|
||||
self.ignoreParentTransitionRequests = ignoreParentTransitionRequests
|
||||
self.contentData = ContentData(
|
||||
context: context,
|
||||
theme: theme,
|
||||
|
|
@ -95,6 +99,7 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
|
|||
content: content
|
||||
)
|
||||
self.update(transition: transition)
|
||||
self.ignoreParentTransitionRequests = false
|
||||
}
|
||||
|
||||
public func updateActivities(activities: ChatTitleComponent.Activities?, transition: ComponentTransition) {
|
||||
|
|
@ -112,7 +117,9 @@ public final class ChatNavigationBarTitleView: UIView, NavigationBarTitleView {
|
|||
}
|
||||
|
||||
private func update(transition: ComponentTransition) {
|
||||
self.requestUpdate?(transition.containedViewLayoutTransition)
|
||||
if !self.ignoreParentTransitionRequests {
|
||||
self.requestUpdate?(transition.containedViewLayoutTransition)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(availableSize: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ final class CraftTableComponent: Component {
|
|||
diameter: 84.0,
|
||||
lineWidth: 5.0,
|
||||
fontSize: 18.0,
|
||||
progress: CGFloat(permilleValue / 10 / 100),
|
||||
progress: CGFloat(permilleValue) / 10.0 / 100.0,
|
||||
value: permilleValue / 10,
|
||||
suffix: "%",
|
||||
isVisible: !component.isCrafting
|
||||
|
|
|
|||
|
|
@ -460,7 +460,7 @@ private final class CraftGiftPageContent: Component {
|
|||
},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
)
|
||||
craftContentHeight += 276.0
|
||||
craftContentHeight += 291.0
|
||||
craftContentHeight += descriptionTextSize.height
|
||||
craftContentHeight += 25.0
|
||||
|
||||
|
|
@ -566,7 +566,7 @@ private final class CraftGiftPageContent: Component {
|
|||
diameter: 48.0,
|
||||
lineWidth: 4.0,
|
||||
fontSize: 10.0,
|
||||
progress: CGFloat(backdropPermille / 10 / 100),
|
||||
progress: CGFloat(backdropPermille) / 10.0 / 100.0,
|
||||
value: backdropPermille / 10,
|
||||
suffix: "%"
|
||||
)
|
||||
|
|
@ -615,7 +615,7 @@ private final class CraftGiftPageContent: Component {
|
|||
contentSize: CGSize(width: 28.0, height: 28.0),
|
||||
lineWidth: 4.0,
|
||||
fontSize: 10.0,
|
||||
progress: CGFloat(symbolPermille / 10 / 100),
|
||||
progress: CGFloat(symbolPermille) / 10.0 / 100.0,
|
||||
value: symbolPermille / 10,
|
||||
suffix: "%"
|
||||
)
|
||||
|
|
@ -1763,8 +1763,15 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
}
|
||||
default:
|
||||
if let navigationController = controller()?.navigationController {
|
||||
var text: String = strings.Login_UnknownError
|
||||
switch error {
|
||||
case .unavailable:
|
||||
text = strings.Gift_Craft_Error_NotAvailable
|
||||
default:
|
||||
break
|
||||
}
|
||||
dismiss(true)
|
||||
let alertController = textAlertController(context: component.context, title: nil, text: strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})])
|
||||
let alertController = textAlertController(context: component.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strings.Common_OK, action: {})])
|
||||
(navigationController.topViewController as? ViewController)?.present(alertController, in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1220,7 +1220,7 @@ final class GiftStoreScreenComponent: Component {
|
|||
}
|
||||
let tonBalance = tonContext.currentState?.balance.value ?? 0
|
||||
if tonBalance == 0 {
|
||||
let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: tonContext)
|
||||
let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: starsContext)
|
||||
controller.push(controller)
|
||||
return
|
||||
}
|
||||
|
|
@ -1242,7 +1242,7 @@ final class GiftStoreScreenComponent: Component {
|
|||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(
|
||||
text: "My Stars",
|
||||
text: presentationData.strings.Gift_Store_Balance_MyStars,
|
||||
textLayout: .secondLineWithValue(formatStarsAmountText(starsBalance, dateTimeFormat: presentationData.dateTimeFormat)),
|
||||
icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Stars"), color: theme.contextMenu.primaryColor) },
|
||||
action: { [weak self] _, f in
|
||||
|
|
@ -1256,7 +1256,7 @@ final class GiftStoreScreenComponent: Component {
|
|||
)))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(
|
||||
text: "My TON",
|
||||
text: presentationData.strings.Gift_Store_Balance_MyTon,
|
||||
textLayout: .secondLineWithValue(formatTonAmountText(tonBalance, dateTimeFormat: presentationData.dateTimeFormat)),
|
||||
icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Ton"), color: theme.contextMenu.primaryColor) },
|
||||
action: { [weak self] _, f in
|
||||
|
|
|
|||
|
|
@ -1013,6 +1013,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
|||
.single(gift.themePeerId)
|
||||
|> then(
|
||||
context.engine.payments.getUniqueStarGift(slug: gift.slug)
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<StarGift.UniqueGift?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { gift in
|
||||
return gift?.themePeerId
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,17 +69,20 @@ public final class GlassControlGroupComponent: Component {
|
|||
}
|
||||
|
||||
public let theme: PresentationTheme
|
||||
public let preferClearGlass: Bool
|
||||
public let background: Background
|
||||
public let items: [Item]
|
||||
public let minWidth: CGFloat
|
||||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
preferClearGlass: Bool,
|
||||
background: Background,
|
||||
items: [Item],
|
||||
minWidth: CGFloat
|
||||
) {
|
||||
self.theme = theme
|
||||
self.preferClearGlass = preferClearGlass
|
||||
self.background = background
|
||||
self.items = items
|
||||
self.minWidth = minWidth
|
||||
|
|
@ -89,6 +92,9 @@ public final class GlassControlGroupComponent: Component {
|
|||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.preferClearGlass != rhs.preferClearGlass {
|
||||
return false
|
||||
}
|
||||
if lhs.background != rhs.background {
|
||||
return false
|
||||
}
|
||||
|
|
@ -151,13 +157,13 @@ public final class GlassControlGroupComponent: Component {
|
|||
switch component.background {
|
||||
case .panel:
|
||||
foregroundColor = component.theme.chat.inputPanel.panelControlColor
|
||||
tintColor = .init(kind: .panel)
|
||||
tintColor = .init(kind: component.preferClearGlass ? .clear : .panel)
|
||||
case .activeTint:
|
||||
foregroundColor = component.theme.list.itemCheckColors.foregroundColor
|
||||
tintColor = .init(kind: .panel, innerColor: component.theme.list.itemCheckColors.fillColor)
|
||||
tintColor = .init(kind: component.preferClearGlass ? .clear : .panel, innerColor: component.theme.list.itemCheckColors.fillColor)
|
||||
case let .color(color):
|
||||
foregroundColor = .white
|
||||
tintColor = .init(kind: .custom(style: .default, color: color))
|
||||
tintColor = .init(kind: .custom(style: component.preferClearGlass ? .clear : .default, color: color))
|
||||
}
|
||||
|
||||
var contentsWidth: CGFloat = 0.0
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
}
|
||||
|
||||
public let theme: PresentationTheme
|
||||
public let preferClearGlass: Bool
|
||||
public let leftItem: Item?
|
||||
public let rightItem: Item?
|
||||
public let centralItem: Item?
|
||||
|
|
@ -41,6 +42,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
preferClearGlass: Bool = false,
|
||||
leftItem: Item?,
|
||||
centralItem: Item?,
|
||||
rightItem: Item?,
|
||||
|
|
@ -49,6 +51,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
tag: AnyObject? = nil
|
||||
) {
|
||||
self.theme = theme
|
||||
self.preferClearGlass = preferClearGlass
|
||||
self.leftItem = leftItem
|
||||
self.centralItem = centralItem
|
||||
self.rightItem = rightItem
|
||||
|
|
@ -61,6 +64,9 @@ public final class GlassControlPanelComponent: Component {
|
|||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.preferClearGlass != rhs.preferClearGlass {
|
||||
return false
|
||||
}
|
||||
if lhs.leftItem != rhs.leftItem {
|
||||
return false
|
||||
}
|
||||
|
|
@ -149,6 +155,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
transition: leftItemTransition,
|
||||
component: AnyComponent(GlassControlGroupComponent(
|
||||
theme: component.theme,
|
||||
preferClearGlass: component.preferClearGlass,
|
||||
background: leftItem.background,
|
||||
items: leftItem.items,
|
||||
minWidth: availableSize.height
|
||||
|
|
@ -198,6 +205,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
transition: rightItemTransition,
|
||||
component: AnyComponent(GlassControlGroupComponent(
|
||||
theme: component.theme,
|
||||
preferClearGlass: component.preferClearGlass,
|
||||
background: rightItem.background,
|
||||
items: rightItem.items,
|
||||
minWidth: availableSize.height
|
||||
|
|
@ -264,6 +272,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
transition: centralItemTransition,
|
||||
component: AnyComponent(GlassControlGroupComponent(
|
||||
theme: component.theme,
|
||||
preferClearGlass: component.preferClearGlass,
|
||||
background: centralItem.background,
|
||||
items: centralItem.items,
|
||||
minWidth: centralItem.keepWide ? 165.0 : availableSize.height
|
||||
|
|
|
|||
|
|
@ -1008,6 +1008,14 @@ public final class NavigationBarImpl: ASDisplayNode, NavigationBar {
|
|||
}
|
||||
}
|
||||
|
||||
if (leftTitleInset == leftInset) != (rightTitleInset == rightInset) {
|
||||
if rightTitleInset == rightInset {
|
||||
rightTitleInset = leftTitleInset
|
||||
} else if leftTitleInset == leftInset {
|
||||
leftTitleInset = rightTitleInset
|
||||
}
|
||||
}
|
||||
|
||||
if self.titleNode.view.superview != nil {
|
||||
let titleSize = self.titleNode.updateLayout(CGSize(width: max(1.0, size.width - max(leftTitleInset, rightTitleInset) * 2.0), height: nominalHeight))
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
swift_library(
|
||||
name = "AccountPeerContextItem",
|
||||
module_name = "AccountPeerContextItem",
|
||||
srcs = glob([
|
||||
"Sources/**/*.swift",
|
||||
]),
|
||||
copts = [
|
||||
"-warnings-as-errors",
|
||||
],
|
||||
deps = [
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit",
|
||||
"//submodules/Display",
|
||||
"//submodules/Postbox",
|
||||
"//submodules/TelegramCore",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/TelegramPresentationData",
|
||||
"//submodules/ComponentFlow",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/AvatarNode",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/AccountContext",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
)
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ContextUI
|
||||
import AvatarNode
|
||||
import EmojiStatusComponent
|
||||
import AccountContext
|
||||
|
||||
public final class AccountPeerContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
let account: Account
|
||||
let peer: EnginePeer
|
||||
let action: (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void
|
||||
|
||||
public init(context: AccountContext, account: Account, peer: EnginePeer, action: @escaping (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void) {
|
||||
self.context = context
|
||||
self.account = account
|
||||
self.peer = peer
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
|
||||
return AccountPeerContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustomNode {
|
||||
private let item: AccountPeerContextItem
|
||||
private let presentationData: PresentationData
|
||||
private let getController: () -> ContextControllerProtocol?
|
||||
private let actionSelected: (ContextMenuActionResult) -> Void
|
||||
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let emojiStatusView: ComponentView<Empty>
|
||||
|
||||
init(presentationData: PresentationData, item: AccountPeerContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
|
||||
self.item = item
|
||||
self.presentationData = presentationData
|
||||
self.getController = getController
|
||||
self.actionSelected = actionSelected
|
||||
|
||||
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0)
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.isAccessibilityElement = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = false
|
||||
let peerTitle = item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
self.textNode.attributedText = NSAttributedString(string: peerTitle, font: textFont, textColor: presentationData.theme.contextMenu.primaryColor)
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0))
|
||||
|
||||
self.emojiStatusView = ComponentView<Empty>()
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.isAccessibilityElement = true
|
||||
self.buttonNode.accessibilityLabel = peerTitle
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
|
||||
let sideInset: CGFloat = 18.0
|
||||
let iconSideInset: CGFloat = 20.0
|
||||
let verticalInset: CGFloat = 11.0
|
||||
|
||||
let iconSize = CGSize(width: 28.0, height: 28.0)
|
||||
|
||||
let standardIconWidth: CGFloat = 32.0
|
||||
var rightTextInset: CGFloat = sideInset
|
||||
if !iconSize.width.isZero {
|
||||
rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 12.0
|
||||
}
|
||||
|
||||
self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer)
|
||||
|
||||
if self.item.peer.emojiStatus != nil {
|
||||
rightTextInset += 32.0
|
||||
}
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))
|
||||
|
||||
return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in
|
||||
let verticalOrigin = floor((size.height - textSize.height) / 2.0)
|
||||
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
var iconContent: EmojiStatusComponent.Content?
|
||||
if case let .user(user) = self.item.peer {
|
||||
if let emojiStatus = user.emojiStatus {
|
||||
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
|
||||
} else if user.isPremium {
|
||||
iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
} else if case let .channel(channel) = self.item.peer {
|
||||
if let emojiStatus = channel.emojiStatus {
|
||||
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
|
||||
}
|
||||
}
|
||||
if let iconContent {
|
||||
let emojiStatusSize = self.emojiStatusView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.item.context,
|
||||
animationCache: self.item.context.animationCache,
|
||||
animationRenderer: self.item.context.animationRenderer,
|
||||
content: iconContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 24.0, height: 24.0)
|
||||
)
|
||||
if let view = self.emojiStatusView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: textFrame.maxX + 2.0, y: textFrame.minY + floor((textFrame.height - emojiStatusSize.height) / 2.0)), size: emojiStatusSize))
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
|
||||
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
})
|
||||
}
|
||||
|
||||
func updateTheme(presentationData: PresentationData) {
|
||||
if let attributedText = self.textNode.attributedText {
|
||||
let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText)
|
||||
updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length))
|
||||
self.textNode.attributedText = updatedAttributedText
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.performAction()
|
||||
}
|
||||
|
||||
func canBeHighlighted() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func setIsHighlighted(_ value: Bool) {
|
||||
}
|
||||
|
||||
func updateIsHighlighted(isHighlighted: Bool) {
|
||||
self.setIsHighlighted(isHighlighted)
|
||||
}
|
||||
|
||||
func performAction() {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
self.item.action(controller, { [weak self] result in
|
||||
self?.actionSelected(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -173,6 +173,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/AvatarComponent",
|
||||
"//submodules/TelegramUI/Components/AlertComponent/AlertTransferHeaderComponent",
|
||||
"//submodules/TelegramUI/Components/HorizontalTabsComponent",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/AccountPeerContextItem",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -417,6 +417,7 @@ private final class PeerInfoPendingPane {
|
|||
sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?,
|
||||
initialStoryFolderId: Int64?,
|
||||
initialGiftCollectionId: Int64?,
|
||||
switchToMediaTarget: PeerInfoSwitchToMediaTarget?,
|
||||
key: PeerInfoPaneKey,
|
||||
hasBecomeReady: @escaping (PeerInfoPaneKey) -> Void,
|
||||
parentController: ViewController?,
|
||||
|
|
@ -520,7 +521,7 @@ private final class PeerInfoPendingPane {
|
|||
openAddStory()
|
||||
}
|
||||
case .media:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .photoOrVideo, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .photoOrVideo, captureProtected: captureProtected, initialFocusMessageIndex: switchToMediaTarget?.kind == .photoVideo ? switchToMediaTarget?.messageIndex : nil)
|
||||
paneNode = visualPaneNode
|
||||
visualPaneNode.openCurrentDate = {
|
||||
openMediaCalendar()
|
||||
|
|
@ -529,15 +530,15 @@ private final class PeerInfoPendingPane {
|
|||
paneDidScroll()
|
||||
}
|
||||
case .files:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .files, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .files, captureProtected: captureProtected, initialFocusMessageIndex: switchToMediaTarget?.kind == .photoVideo ? switchToMediaTarget?.messageIndex : nil)
|
||||
paneNode = visualPaneNode
|
||||
case .links:
|
||||
paneNode = PeerInfoListPaneNode(context: context, updatedPresentationData: updatedPresentationData, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, tagMask: .webPage)
|
||||
case .voice:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .voiceAndVideoMessages, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .voiceAndVideoMessages, captureProtected: captureProtected, initialFocusMessageIndex: nil)
|
||||
paneNode = visualPaneNode
|
||||
case .music:
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .music, captureProtected: captureProtected)
|
||||
let visualPaneNode = PeerInfoVisualMediaPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .music, captureProtected: captureProtected, initialFocusMessageIndex: nil)
|
||||
paneNode = visualPaneNode
|
||||
case .gifs:
|
||||
let visualPaneNode = PeerInfoGifPaneNode(context: context, chatControllerInteraction: chatControllerInteraction, peerId: chatLocationPeerId, chatLocation: chatLocation, chatLocationContextHolder: chatLocationContextHolder, contentType: .gifs)
|
||||
|
|
@ -626,6 +627,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
private var shouldFadeIn = false
|
||||
private var initialStoryFolderId: Int64?
|
||||
private var initialGiftCollectionId: Int64?
|
||||
private var switchToMediaTarget: PeerInfoSwitchToMediaTarget?
|
||||
|
||||
private var isDraggingTabs: Bool = false
|
||||
private var transitionFraction: CGFloat = 0.0
|
||||
|
|
@ -653,7 +655,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
|
||||
private let initialPaneKey: PeerInfoPaneKey?
|
||||
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?, initialGiftCollectionId: Int64?) {
|
||||
init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: PeerId, chatLocation: ChatLocation, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, isMediaOnly: Bool, initialPaneKey: PeerInfoPaneKey?, initialStoryFolderId: Int64?, initialGiftCollectionId: Int64?, switchToMediaTarget: PeerInfoSwitchToMediaTarget?) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.peerId = peerId
|
||||
|
|
@ -664,6 +666,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
self.initialPaneKey = initialPaneKey
|
||||
self.initialStoryFolderId = initialStoryFolderId
|
||||
self.initialGiftCollectionId = initialGiftCollectionId
|
||||
self.switchToMediaTarget = switchToMediaTarget
|
||||
|
||||
self.tabsBackgroundContainer = GlassBackgroundContainerView()
|
||||
self.tabsBackgroundView = GlassBackgroundView()
|
||||
|
|
@ -1002,6 +1005,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
var leftScope = false
|
||||
var initialStoryFolderId: Int64?
|
||||
var initialGiftCollectionId: Int64?
|
||||
var switchToMediaTarget: PeerInfoSwitchToMediaTarget?
|
||||
if case .stories = key {
|
||||
if let initialStoryFolderIdValue = self.initialStoryFolderId {
|
||||
self.initialStoryFolderId = nil
|
||||
|
|
@ -1014,6 +1018,18 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
initialGiftCollectionId = initialGiftCollectionIdValue
|
||||
}
|
||||
}
|
||||
if case .media = key {
|
||||
if let switchToMediaTargetValue = self.switchToMediaTarget, case .photoVideo = switchToMediaTargetValue.kind {
|
||||
self.switchToMediaTarget = nil
|
||||
switchToMediaTarget = switchToMediaTargetValue
|
||||
}
|
||||
}
|
||||
if case .files = key {
|
||||
if let switchToMediaTargetValue = self.switchToMediaTarget, case .file = switchToMediaTargetValue.kind {
|
||||
self.switchToMediaTarget = nil
|
||||
switchToMediaTarget = switchToMediaTargetValue
|
||||
}
|
||||
}
|
||||
let pane = PeerInfoPendingPane(
|
||||
context: self.context,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
|
|
@ -1034,6 +1050,7 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, ASGestureRecognizerDelegat
|
|||
sharedMediaFromForumTopic: self.sharedMediaFromForumTopic,
|
||||
initialStoryFolderId: initialStoryFolderId,
|
||||
initialGiftCollectionId: initialGiftCollectionId,
|
||||
switchToMediaTarget: switchToMediaTarget,
|
||||
key: key,
|
||||
hasBecomeReady: { [weak self] key in
|
||||
let apply: () -> Void = {
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ import GiftViewScreen
|
|||
import PeerMessagesMediaPlaylist
|
||||
import EdgeEffect
|
||||
import Pasteboard
|
||||
import AccountPeerContextItem
|
||||
|
||||
public enum PeerInfoAvatarEditingMode {
|
||||
case generic
|
||||
|
|
@ -212,13 +213,14 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
let chatLocation: ChatLocation
|
||||
let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
|
||||
let switchToStoryFolder: Int64?
|
||||
let switchToMediaTarget: PeerInfoSwitchToMediaTarget?
|
||||
let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?
|
||||
let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
|
||||
|
||||
let isSettings: Bool
|
||||
let isMyProfile: Bool
|
||||
let isMediaOnly: Bool
|
||||
let initialExpandPanes: Bool
|
||||
var initialExpandPanes: Bool
|
||||
|
||||
private(set) var presentationData: PresentationData
|
||||
|
||||
|
|
@ -345,7 +347,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
}
|
||||
private var didSetReady = false
|
||||
|
||||
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?, switchToStoryFolder: Int64?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
|
||||
init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, isMyProfile: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?, profileGiftsContext: ProfileGiftsContext?, starsContext: StarsContext?, tonContext: StarsContext?, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?, switchToStoryFolder: Int64?, switchToMediaTarget: PeerInfoSwitchToMediaTarget?, initialPaneKey: PeerInfoPaneKey?, sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?) {
|
||||
self.controller = controller
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
|
|
@ -362,6 +364,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
self.isMediaOnly = context.account.peerId == peerId && !isSettings && !isMyProfile
|
||||
self.initialExpandPanes = initialPaneKey != nil
|
||||
self.switchToStoryFolder = switchToStoryFolder
|
||||
self.switchToMediaTarget = switchToMediaTarget
|
||||
self.switchToGiftsTarget = switchToGiftsTarget
|
||||
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
|
||||
|
||||
|
|
@ -385,7 +388,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
break
|
||||
}
|
||||
|
||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder, initialGiftCollectionId: switchToGiftCollection)
|
||||
self.paneContainerNode = PeerInfoPaneContainerNode(context: context, updatedPresentationData: controller.updatedPresentationData, peerId: peerId, chatLocation: chatLocation, sharedMediaFromForumTopic: sharedMediaFromForumTopic, chatLocationContextHolder: chatLocationContextHolder, isMediaOnly: self.isMediaOnly, initialPaneKey: initialPaneKey, initialStoryFolderId: switchToStoryFolder, initialGiftCollectionId: switchToGiftCollection, switchToMediaTarget: switchToMediaTarget)
|
||||
|
||||
super.init()
|
||||
|
||||
|
|
@ -2826,6 +2829,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
self.ignoreScrolling = true
|
||||
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: paneAreaExpansionFinalPoint), size: self.scrollNode.bounds.size))
|
||||
self.ignoreScrolling = false
|
||||
self.headerNode.headerEdgeEffectContainer.center = CGPoint(x: 0.0, y: self.scrollNode.view.contentOffset.y)
|
||||
self.updateNavigation(transition: transition, additive: false, animateHeader: true)
|
||||
if let (layout, navigationHeight) = self.validLayout {
|
||||
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: transition, additive: true)
|
||||
|
|
@ -5747,7 +5751,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
|
||||
let navigationBarHeight: CGFloat = !self.isSettings && layout.isModalOverlay ? 68.0 : 60.0
|
||||
let paneContainerTopInset = navigationBarHeight + (layout.statusBarHeight ?? 0.0)
|
||||
self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, topInset: paneContainerTopInset, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition)
|
||||
self.paneContainerNode.update(size: self.paneContainerNode.bounds.size, sideInset: layout.safeInsets.left, topInset: paneContainerTopInset, bottomInset: bottomInset, deviceMetrics: layout.deviceMetrics, visibleHeight: visibleHeight, expansionFraction: self.initialExpandPanes ? 1.0 : effectiveAreaExpansionFraction, presentationData: self.presentationData, data: self.data, areTabsHidden: self.headerNode.customNavigationContentNode != nil, disableTabSwitching: disableTabSwitching, navigationHeight: navigationHeight, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.headerNode.navigationButtonContainer, frame: CGRect(origin: CGPoint(x: layout.safeInsets.left, y: layout.statusBarHeight ?? 0.0), size: CGSize(width: layout.size.width - layout.safeInsets.left * 2.0, height: navigationBarHeight)))
|
||||
var searchBarContainerY: CGFloat = layout.statusBarHeight ?? 0.0
|
||||
|
|
@ -6217,6 +6221,21 @@ public enum PeerInfoSwitchToGiftsTarget {
|
|||
case collection(Int64)
|
||||
}
|
||||
|
||||
public struct PeerInfoSwitchToMediaTarget {
|
||||
public enum Kind {
|
||||
case photoVideo
|
||||
case file
|
||||
}
|
||||
|
||||
public let kind: Kind
|
||||
public let messageIndex: EngineMessage.Index
|
||||
|
||||
public init(kind: Kind, messageIndex: EngineMessage.Index) {
|
||||
self.kind = kind
|
||||
self.messageIndex = messageIndex
|
||||
}
|
||||
}
|
||||
|
||||
public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortcutResponder {
|
||||
let context: AccountContext
|
||||
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
|
|
@ -6237,6 +6256,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
private let switchToGiftsTarget: PeerInfoSwitchToGiftsTarget?
|
||||
private let switchToGroupsInCommon: Bool
|
||||
private let switchToStoryFolder: Int64?
|
||||
private let switchToMediaTarget: PeerInfoSwitchToMediaTarget?
|
||||
private let sharedMediaFromForumTopic: (EnginePeer.Id, Int64)?
|
||||
let chatLocation: ChatLocation
|
||||
private let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
|
||||
|
|
@ -6320,6 +6340,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
switchToGiftsTarget: PeerInfoSwitchToGiftsTarget? = nil,
|
||||
switchToGroupsInCommon: Bool = false,
|
||||
switchToStoryFolder: Int64? = nil,
|
||||
switchToMediaTarget: PeerInfoSwitchToMediaTarget? = nil,
|
||||
) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
|
|
@ -6338,6 +6359,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
self.switchToGiftsTarget = switchToGiftsTarget
|
||||
self.switchToGroupsInCommon = switchToGroupsInCommon
|
||||
self.switchToStoryFolder = switchToStoryFolder
|
||||
self.switchToMediaTarget = switchToMediaTarget
|
||||
self.sharedMediaFromForumTopic = sharedMediaFromForumTopic
|
||||
|
||||
if let forumTopicThread = forumTopicThread {
|
||||
|
|
@ -6683,8 +6705,15 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
initialPaneKey = .groupsInCommon
|
||||
} else if self.switchToStoryFolder != nil {
|
||||
initialPaneKey = .stories
|
||||
} else if let switchToMediaTarget = self.switchToMediaTarget {
|
||||
switch switchToMediaTarget.kind {
|
||||
case .photoVideo:
|
||||
initialPaneKey = .media
|
||||
case .file:
|
||||
initialPaneKey = .files
|
||||
}
|
||||
}
|
||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToGiftsTarget: self.switchToGiftsTarget, switchToStoryFolder: self.switchToStoryFolder, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
|
||||
self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, isMyProfile: self.isMyProfile, hintGroupInCommon: self.hintGroupInCommon, requestsContext: self.requestsContext, profileGiftsContext: self.profileGiftsContext, starsContext: self.starsContext, tonContext: self.tonContext, chatLocation: self.chatLocation, chatLocationContextHolder: self.chatLocationContextHolder, switchToGiftsTarget: self.switchToGiftsTarget, switchToStoryFolder: self.switchToStoryFolder, switchToMediaTarget: self.switchToMediaTarget, initialPaneKey: initialPaneKey, sharedMediaFromForumTopic: self.sharedMediaFromForumTopic)
|
||||
self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 })
|
||||
self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get())
|
||||
self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get())
|
||||
|
|
@ -6933,6 +6962,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc
|
|||
}
|
||||
|
||||
self.controllerNode.refreshHasPersonalChannelsIfNeeded()
|
||||
self.controllerNode.initialExpandPanes = false
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
|
|
@ -89,167 +89,6 @@ extension PeerInfoScreenNode {
|
|||
}
|
||||
}
|
||||
|
||||
final class AccountPeerContextItem: ContextMenuCustomItem {
|
||||
let context: AccountContext
|
||||
let account: Account
|
||||
let peer: EnginePeer
|
||||
let action: (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void
|
||||
|
||||
init(context: AccountContext, account: Account, peer: EnginePeer, action: @escaping (ContextControllerProtocol, @escaping (ContextMenuActionResult) -> Void) -> Void) {
|
||||
self.context = context
|
||||
self.account = account
|
||||
self.peer = peer
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public func node(presentationData: PresentationData, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) -> ContextMenuCustomNode {
|
||||
return AccountPeerContextItemNode(presentationData: presentationData, item: self, getController: getController, actionSelected: actionSelected)
|
||||
}
|
||||
}
|
||||
|
||||
private final class AccountPeerContextItemNode: ASDisplayNode, ContextMenuCustomNode {
|
||||
private let item: AccountPeerContextItem
|
||||
private let presentationData: PresentationData
|
||||
private let getController: () -> ContextControllerProtocol?
|
||||
private let actionSelected: (ContextMenuActionResult) -> Void
|
||||
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let textNode: ImmediateTextNode
|
||||
private let avatarNode: AvatarNode
|
||||
private let emojiStatusView: ComponentView<Empty>
|
||||
|
||||
init(presentationData: PresentationData, item: AccountPeerContextItem, getController: @escaping () -> ContextControllerProtocol?, actionSelected: @escaping (ContextMenuActionResult) -> Void) {
|
||||
self.item = item
|
||||
self.presentationData = presentationData
|
||||
self.getController = getController
|
||||
self.actionSelected = actionSelected
|
||||
|
||||
let textFont = Font.regular(presentationData.listsFontSize.baseDisplaySize * 17.0 / 17.0)
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
self.textNode.isAccessibilityElement = false
|
||||
self.textNode.isUserInteractionEnabled = false
|
||||
self.textNode.displaysAsynchronously = false
|
||||
let peerTitle = item.peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
self.textNode.attributedText = NSAttributedString(string: peerTitle, font: textFont, textColor: presentationData.theme.contextMenu.primaryColor)
|
||||
self.textNode.maximumNumberOfLines = 1
|
||||
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 14.0))
|
||||
|
||||
self.emojiStatusView = ComponentView<Empty>()
|
||||
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.isAccessibilityElement = true
|
||||
self.buttonNode.accessibilityLabel = peerTitle
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.textNode)
|
||||
self.addSubnode(self.avatarNode)
|
||||
self.addSubnode(self.buttonNode)
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
func updateLayout(constrainedWidth: CGFloat, constrainedHeight: CGFloat) -> (CGSize, (CGSize, ContainedViewLayoutTransition) -> Void) {
|
||||
let sideInset: CGFloat = 18.0
|
||||
let iconSideInset: CGFloat = 20.0
|
||||
let verticalInset: CGFloat = 11.0
|
||||
|
||||
let iconSize = CGSize(width: 28.0, height: 28.0)
|
||||
|
||||
let standardIconWidth: CGFloat = 32.0
|
||||
var rightTextInset: CGFloat = sideInset
|
||||
if !iconSize.width.isZero {
|
||||
rightTextInset = max(iconSize.width, standardIconWidth) + iconSideInset + sideInset - 12.0
|
||||
}
|
||||
|
||||
self.avatarNode.setPeer(context: self.item.context, account: self.item.account, theme: self.presentationData.theme, peer: self.item.peer)
|
||||
|
||||
if self.item.peer.emojiStatus != nil {
|
||||
rightTextInset += 32.0
|
||||
}
|
||||
|
||||
let textSize = self.textNode.updateLayout(CGSize(width: constrainedWidth - sideInset - rightTextInset, height: .greatestFiniteMagnitude))
|
||||
|
||||
return (CGSize(width: textSize.width + sideInset + rightTextInset, height: verticalInset * 2.0 + textSize.height), { size, transition in
|
||||
let verticalOrigin = floor((size.height - textSize.height) / 2.0)
|
||||
let textFrame = CGRect(origin: CGPoint(x: iconSideInset + 40.0, y: verticalOrigin), size: textSize)
|
||||
transition.updateFrameAdditive(node: self.textNode, frame: textFrame)
|
||||
|
||||
var iconContent: EmojiStatusComponent.Content?
|
||||
if case let .user(user) = self.item.peer {
|
||||
if let emojiStatus = user.emojiStatus {
|
||||
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
|
||||
} else if user.isPremium {
|
||||
iconContent = .premium(color: self.presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
} else if case let .channel(channel) = self.item.peer {
|
||||
if let emojiStatus = channel.emojiStatus {
|
||||
iconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 28.0, height: 28.0), placeholderColor: self.presentationData.theme.list.mediaPlaceholderColor, themeColor: self.presentationData.theme.list.itemAccentColor, loopMode: .forever)
|
||||
}
|
||||
}
|
||||
if let iconContent {
|
||||
let emojiStatusSize = self.emojiStatusView.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: self.item.context,
|
||||
animationCache: self.item.context.animationCache,
|
||||
animationRenderer: self.item.context.animationRenderer,
|
||||
content: iconContent,
|
||||
isVisibleForAnimations: true,
|
||||
action: nil
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 24.0, height: 24.0)
|
||||
)
|
||||
if let view = self.emojiStatusView.view {
|
||||
if view.superview == nil {
|
||||
self.view.addSubview(view)
|
||||
}
|
||||
transition.updateFrame(view: view, frame: CGRect(origin: CGPoint(x: textFrame.maxX + 2.0, y: textFrame.minY + floor((textFrame.height - emojiStatusSize.height) / 2.0)), size: emojiStatusSize))
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: iconSideInset + floor((standardIconWidth - iconSize.width) / 2.0), y: floor((size.height - iconSize.height) / 2.0)), size: iconSize))
|
||||
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)))
|
||||
})
|
||||
}
|
||||
|
||||
func updateTheme(presentationData: PresentationData) {
|
||||
if let attributedText = self.textNode.attributedText {
|
||||
let updatedAttributedText = NSMutableAttributedString(attributedString: attributedText)
|
||||
updatedAttributedText.addAttribute(.foregroundColor, value: presentationData.theme.contextMenu.primaryColor.cgColor, range: NSRange(location: 0, length: updatedAttributedText.length))
|
||||
self.textNode.attributedText = updatedAttributedText
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
self.performAction()
|
||||
}
|
||||
|
||||
func canBeHighlighted() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func setIsHighlighted(_ value: Bool) {
|
||||
}
|
||||
|
||||
func updateIsHighlighted(isHighlighted: Bool) {
|
||||
self.setIsHighlighted(isHighlighted)
|
||||
}
|
||||
|
||||
func performAction() {
|
||||
guard let controller = self.getController() else {
|
||||
return
|
||||
}
|
||||
self.item.action(controller, { [weak self] result in
|
||||
self?.actionSelected(result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
final class PeerInfoControllerContextReferenceContentSource: ContextReferenceContentSource {
|
||||
let controller: ViewController
|
||||
let sourceView: UIView
|
||||
|
|
|
|||
|
|
@ -1113,6 +1113,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
private let directMediaImageCache: DirectMediaImageCache
|
||||
private var items: SparseItemGrid.Items?
|
||||
private var didUpdateItemsOnce: Bool = false
|
||||
private var initialMessageIndex: EngineMessage.Index?
|
||||
|
||||
private var isDeceleratingAfterTracking = false
|
||||
|
||||
|
|
@ -1164,7 +1165,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
private var presentationData: PresentationData
|
||||
private var presentationDataDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, contentType: ContentType, captureProtected: Bool) {
|
||||
public init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, chatLocation: ChatLocation, chatLocationContextHolder: Atomic<ChatLocationContextHolder?>, contentType: ContentType, captureProtected: Bool, initialFocusMessageIndex: EngineMessage.Index?) {
|
||||
self.context = context
|
||||
self.peerId = peerId
|
||||
self.chatLocation = chatLocation
|
||||
|
|
@ -1173,6 +1174,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
self.contentType = contentType
|
||||
self.contentTypePromise = ValuePromise<ContentType>(contentType)
|
||||
self.stateTag = tagMaskForType(contentType)
|
||||
self.initialMessageIndex = initialFocusMessageIndex
|
||||
|
||||
self.presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
|
|
@ -1234,7 +1236,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
threadId = message.threadId
|
||||
}
|
||||
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, threadId: threadId, tag: tagMaskForType(self.contentType))
|
||||
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, threadId: threadId, tag: tagMaskForType(self.contentType), initialMessageIndex: initialFocusMessageIndex)
|
||||
if threadId == nil {
|
||||
switch contentType {
|
||||
case .photoOrVideo, .photo, .video:
|
||||
|
|
@ -1247,6 +1249,11 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
}
|
||||
|
||||
super.init()
|
||||
|
||||
if self.initialMessageIndex != nil {
|
||||
self.didSetReady = true
|
||||
self.ready.set(.single(true))
|
||||
}
|
||||
|
||||
let _ = (ApplicationSpecificNotice.getSharedMediaScrollingTooltip(accountManager: context.sharedContext.accountManager)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] count in
|
||||
|
|
@ -1720,6 +1727,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
}
|
||||
self.isRequestingView = true
|
||||
var firstTime = true
|
||||
var firstTimeRealData = true
|
||||
let queue = Queue()
|
||||
|
||||
self.listDisposable.set((self.listSource.state
|
||||
|
|
@ -1728,15 +1736,20 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
|
||||
var mappedItems: [SparseItemGrid.Item] = []
|
||||
var mappedHoles: [SparseItemGrid.HoleAnchor] = []
|
||||
for item in list.items {
|
||||
switch item.content {
|
||||
case let .message(message, isLocal):
|
||||
mappedItems.append(VisualMediaItem(index: item.index, message: message, localMonthTimestamp: Month(localTimestamp: message.timestamp + timezoneOffset).packedValue))
|
||||
if !isLocal {
|
||||
mappedHoles.append(VisualMediaHoleAnchor(index: item.index, messageId: message.id, localMonthTimestamp: Month(localTimestamp: message.timestamp + timezoneOffset).packedValue))
|
||||
var totalCount = list.totalCount
|
||||
if list.items.isEmpty && list.isLoading && list.totalCount == 0 {
|
||||
totalCount = 100
|
||||
} else {
|
||||
for item in list.items {
|
||||
switch item.content {
|
||||
case let .message(message, isLocal):
|
||||
mappedItems.append(VisualMediaItem(index: item.index, message: message, localMonthTimestamp: Month(localTimestamp: message.timestamp + timezoneOffset).packedValue))
|
||||
if !isLocal {
|
||||
mappedHoles.append(VisualMediaHoleAnchor(index: item.index, messageId: message.id, localMonthTimestamp: Month(localTimestamp: message.timestamp + timezoneOffset).packedValue))
|
||||
}
|
||||
case let .placeholder(id, timestamp):
|
||||
mappedHoles.append(VisualMediaHoleAnchor(index: item.index, messageId: id, localMonthTimestamp: Month(localTimestamp: timestamp + timezoneOffset).packedValue))
|
||||
}
|
||||
case let .placeholder(id, timestamp):
|
||||
mappedHoles.append(VisualMediaHoleAnchor(index: item.index, messageId: id, localMonthTimestamp: Month(localTimestamp: timestamp + timezoneOffset).packedValue))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1748,7 +1761,7 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
let items = SparseItemGrid.Items(
|
||||
items: mappedItems,
|
||||
holeAnchors: mappedHoles,
|
||||
count: list.totalCount,
|
||||
count: totalCount,
|
||||
itemBinding: strongSelf.itemGridBinding,
|
||||
headerText: nil,
|
||||
snapTopInset: true
|
||||
|
|
@ -1756,24 +1769,34 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
|
||||
let currentSynchronous = synchronous && firstTime
|
||||
let currentReloadAtTop = reloadAtTop && firstTime
|
||||
|
||||
var crossfade = currentReloadAtTop
|
||||
if firstTimeRealData && !list.isLoading {
|
||||
firstTimeRealData = false
|
||||
if !firstTime {
|
||||
crossfade = true
|
||||
}
|
||||
}
|
||||
|
||||
firstTime = false
|
||||
strongSelf.updateHistory(items: items, synchronous: currentSynchronous, reloadAtTop: currentReloadAtTop)
|
||||
strongSelf.updateHistory(items: items, synchronous: currentSynchronous, crossfade: crossfade)
|
||||
strongSelf.isRequestingView = false
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
private func updateHistory(items: SparseItemGrid.Items, synchronous: Bool, reloadAtTop: Bool) {
|
||||
private func updateHistory(items: SparseItemGrid.Items, synchronous: Bool, crossfade: Bool) {
|
||||
self.items = items
|
||||
|
||||
if let (size, topInset, sideInset, bottomInset, deviceMetrics, visibleHeight, isScrollingLockedAtTop, expandProgress, navigationHeight, presentationData) = self.currentParams {
|
||||
var gridSnapshot: UIView?
|
||||
if reloadAtTop {
|
||||
if crossfade {
|
||||
gridSnapshot = self.itemGrid.view.snapshotView(afterScreenUpdates: false)
|
||||
}
|
||||
self.update(size: size, topInset: topInset, sideInset: sideInset, bottomInset: bottomInset, deviceMetrics: deviceMetrics, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, navigationHeight: navigationHeight, presentationData: presentationData, synchronous: false, transition: .immediate)
|
||||
if let gridSnapshot = gridSnapshot {
|
||||
self.view.addSubview(gridSnapshot)
|
||||
self.itemGrid.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
gridSnapshot.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak gridSnapshot] _ in
|
||||
gridSnapshot?.removeFromSuperview()
|
||||
})
|
||||
|
|
@ -2194,6 +2217,23 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode,
|
|||
}
|
||||
|
||||
self.itemGrid.update(size: size, insets: UIEdgeInsets(top: topInset, left: sideInset, bottom: bottomInset, right: sideInset), useSideInsets: !isList, scrollIndicatorInsets: UIEdgeInsets(top: 0.0, left: sideInset, bottom: bottomInset, right: sideInset), lockScrollingAtTop: isScrollingLockedAtTop, fixedItemHeight: fixedItemHeight, fixedItemAspect: nil, items: items, theme: self.itemGridBinding.chatPresentationData.theme.theme, synchronous: wasFirstTime ? .full : .none)
|
||||
if let initialMessageIndexValue = self.initialMessageIndex, items.items.contains(where: { item in
|
||||
if let _ = item as? VisualMediaItem {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
self.initialMessageIndex = nil
|
||||
for item in items.items {
|
||||
if let item = item as? VisualMediaItem {
|
||||
if item.message.index <= initialMessageIndexValue {
|
||||
self.itemGrid.scrollToItem(at: item.index, force: true)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ swift_library(
|
|||
"//submodules/Components/BundleIconComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/Markdown",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import Foundation
|
|||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import ViewControllerComponent
|
||||
import AccountContext
|
||||
import SheetComponent
|
||||
import ButtonComponent
|
||||
import TelegramCore
|
||||
import LottieComponent
|
||||
|
||||
private final class ArchiveInfoSheetContentComponent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -36,6 +37,9 @@ private final class ArchiveInfoSheetContentComponent: Component {
|
|||
private let content = ComponentView<Empty>()
|
||||
private let button = ComponentView<Empty>()
|
||||
|
||||
fileprivate let playButtonAnimation = ActionSlot<Void>()
|
||||
private var didPlayAnimation = false
|
||||
|
||||
private var component: ArchiveInfoSheetContentComponent?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
|
|
@ -76,16 +80,34 @@ private final class ArchiveInfoSheetContentComponent: Component {
|
|||
contentHeight += contentSize.height
|
||||
contentHeight += 30.0
|
||||
|
||||
var buttonTitle: [AnyComponentWithIdentity<Empty>] = []
|
||||
buttonTitle.append(AnyComponentWithIdentity(id: 0, component: AnyComponent(LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "anim_ok"),
|
||||
color: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
startingPosition: .begin,
|
||||
size: CGSize(width: 28.0, height: 28.0),
|
||||
playOnce: playButtonAnimation
|
||||
))))
|
||||
buttonTitle.append(AnyComponentWithIdentity(id: 1, component: AnyComponent(ButtonTextContentComponent(
|
||||
text: environment.strings.ArchiveInfo_CloseAction,
|
||||
badge: 0,
|
||||
textColor: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
badgeBackground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
badgeForeground: environment.theme.list.itemCheckColors.fillColor
|
||||
))))
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let buttonSize = self.button.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
style: .glass,
|
||||
color: environment.theme.list.itemCheckColors.fillColor,
|
||||
foreground: environment.theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: environment.theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.8)
|
||||
),
|
||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
||||
Text(text: environment.strings.ArchiveInfo_CloseAction, font: Font.semibold(17.0), color: environment.theme.list.itemCheckColors.foregroundColor)
|
||||
content: AnyComponentWithIdentity(id: "ok", component: AnyComponent(
|
||||
HStack(buttonTitle, spacing: 2.0)
|
||||
)),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
|
|
@ -97,20 +119,19 @@ private final class ArchiveInfoSheetContentComponent: Component {
|
|||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0)
|
||||
containerSize: CGSize(width: availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: buttonSize)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonInsets.left, y: contentHeight), size: buttonSize)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
|
||||
self.playButtonAnimation.invoke(Void())
|
||||
}
|
||||
transition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
contentHeight += buttonSize.height
|
||||
|
||||
let bottomPanelPadding: CGFloat = 12.0
|
||||
let bottomInset: CGFloat = environment.safeInsets.bottom > 0.0 ? environment.safeInsets.bottom + 5.0 : bottomPanelPadding
|
||||
contentHeight += bottomInset
|
||||
contentHeight += buttonInsets.bottom
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
}
|
||||
|
|
@ -224,6 +245,7 @@ private final class ArchiveInfoScreenComponent: Component {
|
|||
})
|
||||
}
|
||||
)),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.actionSheet.opaqueItemBackgroundColor),
|
||||
animateOut: self.sheetAnimateOut
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@ public class WallpaperGalleryController: ViewController {
|
|||
}, currentItemNode: { [weak self] in
|
||||
return self?.galleryNode.pager.centralItemNode()
|
||||
})
|
||||
self.displayNode = WallpaperGalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, pageGap: 0.0, disableTapNavigation: true)
|
||||
self.displayNode = WallpaperGalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, titleView: nil, pageGap: 0.0, disableTapNavigation: true)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
(self.displayNode as? WallpaperGalleryControllerNode)?.nativeStatusBar = self.statusBar
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Navigation/Disclosure.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Navigation/Disclosure.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "openicon.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Navigation/Disclosure.imageset/openicon.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Navigation/Disclosure.imageset/openicon.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -456,6 +456,25 @@ func updateChatPresentationInterfaceStateImpl(
|
|||
|
||||
selfController.tempHideAccessoryPanels = selfController.presentationInterfaceState.search != nil
|
||||
|
||||
if let chatTitleContent = selfController.contentData?.state.chatTitleContent {
|
||||
var titleTransition = ComponentTransition(transition)
|
||||
if case .messageOptions = selfController.subject {
|
||||
titleTransition = titleTransition.withAnimation(.none)
|
||||
}
|
||||
selfController.chatTitleView?.update(
|
||||
context: selfController.context,
|
||||
theme: selfController.presentationData.theme,
|
||||
preferClearGlass: selfController.presentationInterfaceState.preferredGlassType == .clear,
|
||||
wallpaper: selfController.presentationInterfaceState.chatWallpaper,
|
||||
strings: selfController.presentationData.strings,
|
||||
dateTimeFormat: selfController.presentationData.dateTimeFormat,
|
||||
nameDisplayOrder: selfController.presentationData.nameDisplayOrder,
|
||||
content: chatTitleContent,
|
||||
transition: titleTransition,
|
||||
ignoreParentTransitionRequests: true
|
||||
)
|
||||
}
|
||||
|
||||
if selfController.isNodeLoaded {
|
||||
selfController.chatDisplayNode.updateChatPresentationInterfaceState(updatedChatPresentationInterfaceState, transition: transition, interactive: interactive, completion: completion)
|
||||
} else {
|
||||
|
|
@ -609,23 +628,5 @@ func updateChatPresentationInterfaceStateImpl(
|
|||
}
|
||||
}
|
||||
|
||||
if let chatTitleContent = selfController.contentData?.state.chatTitleContent {
|
||||
var titleTransition = ComponentTransition(transition)
|
||||
if case .messageOptions = selfController.subject {
|
||||
titleTransition = titleTransition.withAnimation(.none)
|
||||
}
|
||||
selfController.chatTitleView?.update(
|
||||
context: selfController.context,
|
||||
theme: selfController.presentationData.theme,
|
||||
preferClearGlass: selfController.presentationInterfaceState.preferredGlassType == .clear,
|
||||
wallpaper: selfController.presentationInterfaceState.chatWallpaper,
|
||||
strings: selfController.presentationData.strings,
|
||||
dateTimeFormat: selfController.presentationData.dateTimeFormat,
|
||||
nameDisplayOrder: selfController.presentationData.nameDisplayOrder,
|
||||
content: chatTitleContent,
|
||||
transition: titleTransition
|
||||
)
|
||||
}
|
||||
|
||||
selfController.stateUpdated?(transition)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1204,15 +1204,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
self.push(controller)
|
||||
return true
|
||||
case .starGift, .starGiftUnique:
|
||||
let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
||||
Queue.mainQueue().after(0.15) {
|
||||
if let self {
|
||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||
self.push(controller)
|
||||
if case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = action.action, case let .unique(uniqueGift) = gift, uniqueGift.flags.contains(.isBurned) {
|
||||
self.present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: self.presentationData.strings.Resolve_GiftErrorBurned, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
} else {
|
||||
let controller = self.context.sharedContext.makeGiftViewScreen(context: self.context, message: EngineMessage(message), shareStory: { [weak self] uniqueGift in
|
||||
Queue.mainQueue().after(0.15) {
|
||||
if let self {
|
||||
let controller = self.context.sharedContext.makeStorySharingScreen(context: self.context, subject: .gift(uniqueGift), parentController: self)
|
||||
self.push(controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
self.push(controller)
|
||||
})
|
||||
self.push(controller)
|
||||
}
|
||||
return true
|
||||
case .giftStars:
|
||||
let controller = self.context.sharedContext.makeStarsGiftScreen(context: self.context, message: EngineMessage(message))
|
||||
|
|
@ -1377,189 +1381,232 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
}
|
||||
}
|
||||
|
||||
let openChatMessageParams = OpenChatMessageParams(context: context, updatedPresentationData: self.updatedPresentationData, chatLocation: openChatLocation, chatFilterTag: chatFilterTag, chatLocationContextHolder: self.chatLocationContextHolder, message: message, mediaIndex: params.mediaIndex, standalone: standalone, reverseMessageGalleryOrder: false, mode: mode, navigationController: self.effectiveNavigationController, dismissInput: { [weak self] in
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
}, present: { [weak self] c, a, i in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
if case .current = i {
|
||||
c.presentationArguments = a
|
||||
c.statusBar.alphaUpdated = { [weak self] transition in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.updateStatusBarPresentation(animated: transition.isAnimated)
|
||||
}
|
||||
self.galleryPresentationContext.present(c, on: PresentationSurfaceLevel(rawValue: 0), blockInteraction: true, completion: {})
|
||||
} else {
|
||||
self.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
}
|
||||
}, transitionNode: { [weak self] messageId, media, adjustRect in
|
||||
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
if let self {
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if let result = itemNode.transitionNode(id: messageId, media: media, adjustRect: adjustRect) {
|
||||
selectedNode = result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return selectedNode
|
||||
}, addToTransitionSurface: { [weak self] view in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let contextController = self.currentContextController {
|
||||
contextController.view.addSubview(view)
|
||||
} else {
|
||||
self.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: self.chatDisplayNode.historyNode.view)
|
||||
}
|
||||
}, openUrl: { [weak self] url in
|
||||
self?.openUrl(url, concealed: false, skipConcealedAlert: isLocation, message: nil)
|
||||
}, openPeer: { [weak self] peer, navigation in
|
||||
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
||||
}, callPeer: { [weak self] peerId, isVideo in
|
||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||
}, openConferenceCall: { [weak self] message in
|
||||
self?.controllerInteraction?.openConferenceCall(message)
|
||||
}, enqueueMessage: { [weak self] message in
|
||||
self?.sendMessages([message])
|
||||
}, sendSticker: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] fileReference, sourceNode, sourceRect in
|
||||
return self?.controllerInteraction?.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) ?? false
|
||||
} : nil, sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in
|
||||
self?.controllerInteraction?.sendEmoji(text, attribute, false)
|
||||
} : nil, setupTemporaryHiddenMedia: { [weak self] signal, centralIndex, galleryMedia in
|
||||
if let self {
|
||||
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] entry in
|
||||
if let self, let controllerInteraction = self.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let entry = entry as? InstantPageGalleryEntry, entry.index == centralIndex {
|
||||
messageIdAndMedia[message.id] = [galleryMedia]
|
||||
}
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}, chatAvatarHiddenMedia: { [weak self] signal, media in
|
||||
if let self {
|
||||
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in
|
||||
if let self, let controllerInteraction = self.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let messageId = messageId {
|
||||
messageIdAndMedia[messageId] = [media]
|
||||
}
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}, actionInteraction: GalleryControllerActionInteraction(
|
||||
openUrl: { [weak self] url, concealed, forceExternal in
|
||||
if let self {
|
||||
self.openUrl(url, concealed: concealed, forceExternal: forceExternal, message: nil)
|
||||
}
|
||||
}, openUrlIn: { [weak self] url in
|
||||
if let self {
|
||||
self.openUrlIn(url)
|
||||
}
|
||||
}, openPeerMention: { [weak self] mention in
|
||||
if let self {
|
||||
self.controllerInteraction?.openPeerMention(mention, nil)
|
||||
}
|
||||
}, openPeer: { [weak self] peer in
|
||||
if let self {
|
||||
self.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
}, openHashtag: { [weak self] peerName, hashtag in
|
||||
if let self {
|
||||
self.controllerInteraction?.openHashtag(peerName, hashtag)
|
||||
}
|
||||
}, openBotCommand: { [weak self] command in
|
||||
if let self {
|
||||
self.controllerInteraction?.sendBotCommand(nil, command)
|
||||
}
|
||||
}, openAd: { [weak self] messageId in
|
||||
if let self {
|
||||
self.controllerInteraction?.activateAdAction(messageId, nil, true, true)
|
||||
}
|
||||
}, addContact: { [weak self] phoneNumber in
|
||||
if let self {
|
||||
self.controllerInteraction?.addContact(phoneNumber)
|
||||
}
|
||||
}, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var storedState: MediaPlaybackStoredState?
|
||||
if let timestamp = timestamp {
|
||||
storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate))
|
||||
}
|
||||
let _ = updateMediaPlaybackStoredStateInteractively(engine: self.context.engine, messageId: messageId, state: storedState).startStandalone()
|
||||
}, editMedia: { [weak self] messageId, snapshots, transitionCompletion in
|
||||
let openChatMessageParams = OpenChatMessageParams(
|
||||
context: context,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
chatLocation: openChatLocation,
|
||||
chatFilterTag: chatFilterTag,
|
||||
chatLocationContextHolder: self.chatLocationContextHolder,
|
||||
message: message,
|
||||
mediaIndex: params.mediaIndex,
|
||||
standalone: standalone,
|
||||
reverseMessageGalleryOrder: false,
|
||||
mode: mode,
|
||||
navigationController: self.effectiveNavigationController, dismissInput: { [weak self] in
|
||||
self?.chatDisplayNode.dismissInput()
|
||||
},
|
||||
present: { [weak self] c, a, i in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] message in
|
||||
guard let self, let message = message else {
|
||||
return
|
||||
if case .current = i {
|
||||
c.presentationArguments = a
|
||||
c.statusBar.alphaUpdated = { [weak self] transition in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.updateStatusBarPresentation(animated: transition.isAnimated)
|
||||
}
|
||||
|
||||
var mediaReference: AnyMediaReference?
|
||||
for media in message.media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
mediaReference = AnyMediaReference.standalone(media: image)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
mediaReference = AnyMediaReference.standalone(media: file)
|
||||
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||
if let image = content.image {
|
||||
mediaReference = AnyMediaReference.standalone(media: image)
|
||||
} else if let file = content.file {
|
||||
mediaReference = AnyMediaReference.standalone(media: file)
|
||||
self.galleryPresentationContext.present(c, on: PresentationSurfaceLevel(rawValue: 0), blockInteraction: true, completion: {})
|
||||
} else {
|
||||
self.present(c, in: .window(.root), with: a, blockInteraction: true)
|
||||
}
|
||||
},
|
||||
transitionNode: { [weak self] messageId, media, adjustRect in
|
||||
var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))?
|
||||
if let self {
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
if let result = itemNode.transitionNode(id: messageId, media: media, adjustRect: adjustRect) {
|
||||
selectedNode = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] {
|
||||
legacyMediaEditor(context: self.context, peer: peer, threadTitle: self.contentData?.state.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: {
|
||||
transitionCompletion()
|
||||
}, getCaptionPanelView: { [weak self] in
|
||||
return self?.getCaptionPanelView(isFile: false)
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in
|
||||
if let self {
|
||||
var parameters: ChatSendMessageActionSheetController.SendParameters?
|
||||
if isCaptionAbove {
|
||||
parameters = ChatSendMessageActionSheetController.SendParameters(effect: nil, textIsAboveMedia: true)
|
||||
}
|
||||
self.enqueueMediaMessages(signals: signals, silentPosting: false, parameters: parameters)
|
||||
}
|
||||
return selectedNode
|
||||
},
|
||||
addToTransitionSurface: { [weak self] view in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if let contextController = self.currentContextController {
|
||||
contextController.view.addSubview(view)
|
||||
} else {
|
||||
self.chatDisplayNode.historyNode.view.superview?.insertSubview(view, aboveSubview: self.chatDisplayNode.historyNode.view)
|
||||
}
|
||||
},
|
||||
openUrl: { [weak self] url in
|
||||
self?.openUrl(url, concealed: false, skipConcealedAlert: isLocation, message: nil)
|
||||
},
|
||||
openPeer: { [weak self] peer, navigation in
|
||||
self?.openPeer(peer: EnginePeer(peer), navigation: navigation, fromMessage: nil)
|
||||
},
|
||||
callPeer: { [weak self] peerId, isVideo in
|
||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||
},
|
||||
openConferenceCall: { [weak self] message in
|
||||
self?.controllerInteraction?.openConferenceCall(message)
|
||||
},
|
||||
enqueueMessage: { [weak self] message in
|
||||
self?.sendMessages([message])
|
||||
},
|
||||
sendSticker: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] fileReference, sourceNode, sourceRect in
|
||||
return self?.controllerInteraction?.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil, []) ?? false
|
||||
} : nil,
|
||||
sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in
|
||||
self?.controllerInteraction?.sendEmoji(text, attribute, false)
|
||||
} : nil,
|
||||
setupTemporaryHiddenMedia: { [weak self] signal, centralIndex, galleryMedia in
|
||||
if let self {
|
||||
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] entry in
|
||||
if let self, let controllerInteraction = self.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let entry = entry as? InstantPageGalleryEntry, entry.index == centralIndex {
|
||||
messageIdAndMedia[message.id] = [galleryMedia]
|
||||
}
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
})
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
},
|
||||
chatAvatarHiddenMedia: { [weak self] signal, media in
|
||||
if let self {
|
||||
self.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).startStrict(next: { [weak self] messageId in
|
||||
if let self, let controllerInteraction = self.controllerInteraction {
|
||||
var messageIdAndMedia: [MessageId: [Media]] = [:]
|
||||
|
||||
if let messageId = messageId {
|
||||
messageIdAndMedia[messageId] = [media]
|
||||
}
|
||||
|
||||
controllerInteraction.hiddenMedia = messageIdAndMedia
|
||||
|
||||
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
|
||||
if let itemNode = itemNode as? ChatMessageItemView {
|
||||
itemNode.updateHiddenMedia()
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
},
|
||||
actionInteraction: GalleryControllerActionInteraction(
|
||||
openUrl: { [weak self] url, concealed, forceExternal in
|
||||
if let self {
|
||||
self.openUrl(url, concealed: concealed, forceExternal: forceExternal, message: nil)
|
||||
}
|
||||
})
|
||||
}, updateCanReadHistory: { [weak self] canReadHistory in
|
||||
self?.canReadHistory.set(canReadHistory)
|
||||
}),
|
||||
},
|
||||
openUrlIn: { [weak self] url in
|
||||
if let self {
|
||||
self.openUrlIn(url)
|
||||
}
|
||||
},
|
||||
openPeerMention: { [weak self] mention in
|
||||
if let self {
|
||||
self.controllerInteraction?.openPeerMention(mention, nil)
|
||||
}
|
||||
},
|
||||
openPeer: { [weak self] peer in
|
||||
if let self {
|
||||
self.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
},
|
||||
openHashtag: { [weak self] peerName, hashtag in
|
||||
if let self {
|
||||
self.controllerInteraction?.openHashtag(peerName, hashtag)
|
||||
}
|
||||
},
|
||||
openBotCommand: { [weak self] command in
|
||||
if let self {
|
||||
self.controllerInteraction?.sendBotCommand(nil, command)
|
||||
}
|
||||
},
|
||||
openAd: { [weak self] messageId in
|
||||
if let self {
|
||||
self.controllerInteraction?.activateAdAction(messageId, nil, true, true)
|
||||
}
|
||||
},
|
||||
addContact: { [weak self] phoneNumber in
|
||||
if let self {
|
||||
self.controllerInteraction?.addContact(phoneNumber)
|
||||
}
|
||||
},
|
||||
storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
var storedState: MediaPlaybackStoredState?
|
||||
if let timestamp = timestamp {
|
||||
storedState = MediaPlaybackStoredState(timestamp: timestamp, playbackRate: AudioPlaybackRate(playbackRate))
|
||||
}
|
||||
let _ = updateMediaPlaybackStoredStateInteractively(engine: self.context.engine, messageId: messageId, state: storedState).startStandalone()
|
||||
},
|
||||
editMedia: { [weak self] messageId, snapshots, transitionCompletion in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: messageId))
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] message in
|
||||
guard let self, let message = message else {
|
||||
return
|
||||
}
|
||||
|
||||
var mediaReference: AnyMediaReference?
|
||||
for media in message.media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
mediaReference = AnyMediaReference.standalone(media: image)
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
mediaReference = AnyMediaReference.standalone(media: file)
|
||||
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
|
||||
if let image = content.image {
|
||||
mediaReference = AnyMediaReference.standalone(media: image)
|
||||
} else if let file = content.file {
|
||||
mediaReference = AnyMediaReference.standalone(media: file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let mediaReference = mediaReference, let peer = message.peers[message.id.peerId] {
|
||||
legacyMediaEditor(context: self.context, peer: peer, threadTitle: self.contentData?.state.threadInfo?.title, media: mediaReference, mode: .draw, initialCaption: NSAttributedString(), snapshots: snapshots, transitionCompletion: {
|
||||
transitionCompletion()
|
||||
}, getCaptionPanelView: { [weak self] in
|
||||
return self?.getCaptionPanelView(isFile: false)
|
||||
}, sendMessagesWithSignals: { [weak self] signals, _, _, isCaptionAbove in
|
||||
if let self {
|
||||
var parameters: ChatSendMessageActionSheetController.SendParameters?
|
||||
if isCaptionAbove {
|
||||
parameters = ChatSendMessageActionSheetController.SendParameters(effect: nil, textIsAboveMedia: true)
|
||||
}
|
||||
self.enqueueMediaMessages(signals: signals, silentPosting: false, parameters: parameters)
|
||||
}
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
updateCanReadHistory: { [weak self] canReadHistory in
|
||||
self?.canReadHistory.set(canReadHistory)
|
||||
}
|
||||
),
|
||||
navigateToMessageContext: { [weak self] message in
|
||||
Task {
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
await self.openMediaMessageContext(message: message)
|
||||
}
|
||||
},
|
||||
getSourceRect: { [weak self] in
|
||||
guard let self else {
|
||||
return nil
|
||||
|
|
@ -2112,7 +2159,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||
if let context = self?.context, let navigationController = self?.effectiveNavigationController {
|
||||
let _ = context.sharedContext.navigateToForumThread(context: context, peerId: peerId, threadId: threadId, messageId: messageId, navigationController: navigationController, activateInput: nil, scrollToEndIfExists: false, keepStack: .always, animated: true).startStandalone()
|
||||
}
|
||||
}, tapMessage: nil, clickThroughMessage: { [weak self] view, location in
|
||||
}, tapMessage: nil,
|
||||
clickThroughMessage: { [weak self] view, location in
|
||||
self?.chatDisplayNode.dismissInput(view: view, location: location)
|
||||
}, toggleMessagesSelection: { [weak self] ids, value in
|
||||
guard let strongSelf = self, strongSelf.isNodeLoaded else {
|
||||
|
|
|
|||
|
|
@ -137,4 +137,36 @@ extension ChatControllerImpl {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openMediaMessageContext(message: EngineMessage) async {
|
||||
guard let peer = await self.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: message.id.peerId)
|
||||
).get() else {
|
||||
return
|
||||
}
|
||||
|
||||
var kind: PeerInfoControllerMode.PeerInfoMediaKind?
|
||||
if message.tags.contains(MessageTags.photoOrVideo) {
|
||||
kind = .photoVideo
|
||||
} else if message.tags.contains(MessageTags.file) {
|
||||
kind = .file
|
||||
}
|
||||
|
||||
guard let kind else {
|
||||
return
|
||||
}
|
||||
|
||||
let peerInfoController = self.context.sharedContext.makePeerInfoController(
|
||||
context: self.context,
|
||||
updatedPresentationData: self.updatedPresentationData,
|
||||
peer: peer._asPeer(),
|
||||
mode: .media(kind: kind, messageIndex: message.index),
|
||||
avatarInitiallyExpanded: false,
|
||||
fromChat: true,
|
||||
requestsContext: nil
|
||||
)
|
||||
if let peerInfoController {
|
||||
self.push(peerInfoController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -358,6 +358,10 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||
return nil
|
||||
})
|
||||
|
||||
gallery.navigateToMessageContext = { message in
|
||||
params.navigateToMessageContext?(message)
|
||||
}
|
||||
|
||||
params.present(gallery, arguments, presentInCurrent ? .current : .window(.root))
|
||||
})
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1632,33 +1632,36 @@ func openResolvedUrlImpl(
|
|||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
})
|
||||
case let .collectible(gift):
|
||||
if let gift {
|
||||
var dismissedImpl: (() -> Void)?
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
let updateExternalController = storyProgressPauseContext.update
|
||||
dismissedImpl = {
|
||||
updateExternalController(nil)
|
||||
}
|
||||
case let .collectible(result):
|
||||
switch result {
|
||||
case let .gift(gift):
|
||||
var dismissedImpl: (() -> Void)?
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
let updateExternalController = storyProgressPauseContext.update
|
||||
dismissedImpl = {
|
||||
updateExternalController(nil)
|
||||
}
|
||||
let controller = context.sharedContext.makeGiftViewScreen(context: context, gift: gift, shareStory: { [weak navigationController] uniqueGift in
|
||||
Queue.mainQueue().after(0.15) {
|
||||
if let lastController = navigationController?.viewControllers.last as? ViewController {
|
||||
let controller = context.sharedContext.makeStorySharingScreen(context: context, subject: .gift(gift), parentController: lastController)
|
||||
navigationController?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
}, openChatTheme: nil, dismissed: {
|
||||
dismissedImpl?()
|
||||
})
|
||||
navigationController?.pushViewController(controller)
|
||||
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
storyProgressPauseContext.update(controller)
|
||||
}
|
||||
} else {
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
let controller = context.sharedContext.makeGiftViewScreen(context: context, gift: gift, shareStory: { [weak navigationController] uniqueGift in
|
||||
Queue.mainQueue().after(0.15) {
|
||||
if let lastController = navigationController?.viewControllers.last as? ViewController {
|
||||
let controller = context.sharedContext.makeStorySharingScreen(context: context, subject: .gift(gift), parentController: lastController)
|
||||
navigationController?.pushViewController(controller)
|
||||
}
|
||||
}
|
||||
}, openChatTheme: nil, dismissed: {
|
||||
dismissedImpl?()
|
||||
})
|
||||
navigationController?.pushViewController(controller)
|
||||
|
||||
if let storyProgressPauseContext = contentContext as? StoryProgressPauseContext {
|
||||
storyProgressPauseContext.update(controller)
|
||||
}
|
||||
case .alreadyBurned:
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Resolve_GiftErrorBurned, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
case .invalidSlug:
|
||||
present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: presentationData.strings.Resolve_GiftErrorNotFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
}
|
||||
case let .auction(auctionContext):
|
||||
if let auctionContext, case let .generic(gift) = auctionContext.gift {
|
||||
if !auctionContext.isFinished, let currentBidPeerId = auctionContext.currentBidPeerId {
|
||||
|
|
@ -1818,8 +1821,27 @@ func openResolvedUrlImpl(
|
|||
if case .request = result {
|
||||
var dismissImpl: (() -> Void)?
|
||||
let controller = AuthConfirmationScreen(context: context, subject: result, completion: { allowWriteAccess, sharePhoneNumber in
|
||||
let _ = context.engine.messages.acceptMessageActionUrlAuth(subject: .url(url), allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber).start(next: { _ in
|
||||
let _ = (context.engine.messages.acceptMessageActionUrlAuth(subject: .url(url), allowWriteAccess: allowWriteAccess, sharePhoneNumber: sharePhoneNumber)
|
||||
|> deliverOnMainQueue).start(next: { _ in
|
||||
dismissImpl?()
|
||||
|
||||
Queue.mainQueue().after(0.3) {
|
||||
let text: String
|
||||
if case let .request(domain, _, _, flags) = result {
|
||||
if flags.contains(.requestPhoneNumber) && !sharePhoneNumber {
|
||||
text = presentationData.strings.AuthConfirmation_LoginSuccess_TextNoNumber(domain).string
|
||||
} else {
|
||||
text = presentationData.strings.AuthConfirmation_LoginSuccess_Text(domain).string
|
||||
}
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginSuccess_Title, text: text, cancel: nil, destructive: false), action: { _ in return true })
|
||||
(navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
}, error: { _ in
|
||||
if case let .request(domain, _, _, _) = result {
|
||||
let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, cancel: nil, destructive: false), action: { _ in return true })
|
||||
(navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root))
|
||||
}
|
||||
})
|
||||
})
|
||||
navigationController?.pushViewController(controller)
|
||||
|
|
|
|||
|
|
@ -4114,8 +4114,23 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
}
|
||||
|
||||
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
||||
var switchToMediaTarget: PeerInfoSwitchToMediaTarget?
|
||||
switch mode {
|
||||
case let .media(kind, index):
|
||||
let mappedKind: PeerInfoSwitchToMediaTarget.Kind
|
||||
switch kind {
|
||||
case .photoVideo:
|
||||
mappedKind = .photoVideo
|
||||
case .file:
|
||||
mappedKind = .file
|
||||
}
|
||||
switchToMediaTarget = PeerInfoSwitchToMediaTarget(kind: mappedKind, messageIndex: index)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if let _ = peer as? TelegramGroup {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], switchToMediaTarget: switchToMediaTarget)
|
||||
} else if let _ = peer as? TelegramChannel {
|
||||
var forumTopicThread: ChatReplyThreadMessage?
|
||||
var switchToRecommendedChannels = false
|
||||
|
|
@ -4138,7 +4153,7 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
|||
default:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder)
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToMediaTarget: switchToMediaTarget)
|
||||
} else if peer is TelegramUser {
|
||||
var nearbyPeerDistance: Int32?
|
||||
var reactionSourceMessageId: MessageId?
|
||||
|
|
@ -4191,9 +4206,9 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation
|
|||
default:
|
||||
break
|
||||
}
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder)
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, isMyProfile: isMyProfile, hintGroupInCommon: hintGroupInCommon, forumTopicThread: forumTopicThread, sharedMediaFromForumTopic: sharedMediaFromForumTopic, switchToGiftsTarget: switchToGiftsTarget, switchToGroupsInCommon: switchToGroupsInCommon, switchToStoryFolder: switchToStoryFolder, switchToMediaTarget: switchToMediaTarget)
|
||||
} else if peer is TelegramSecretChat {
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [])
|
||||
return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], switchToMediaTarget: switchToMediaTarget)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,12 @@ typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) {
|
|||
|
||||
@interface CALayerSpringParametersOverrideParameters : NSObject
|
||||
|
||||
- (instancetype _Nonnull)init;
|
||||
|
||||
@end
|
||||
|
||||
@interface CALayerSpringParametersOverrideParametersSpring : CALayerSpringParametersOverrideParameters
|
||||
|
||||
@property (nonatomic, readonly) CGFloat stiffness;
|
||||
@property (nonatomic, readonly) CGFloat damping;
|
||||
@property (nonatomic, readonly) double duration;
|
||||
|
|
@ -36,6 +42,15 @@ typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) {
|
|||
|
||||
@end
|
||||
|
||||
@interface CALayerSpringParametersOverrideParametersCustomCurve : CALayerSpringParametersOverrideParameters
|
||||
|
||||
@property (nonatomic, readonly) CGPoint cp1;
|
||||
@property (nonatomic, readonly) CGPoint cp2;
|
||||
|
||||
- (instancetype _Nonnull)initWithCp1:(CGPoint)cp1 cp2:(CGPoint)cp2;
|
||||
|
||||
@end
|
||||
|
||||
@interface CALayerSpringParametersOverride : NSObject
|
||||
|
||||
@property (nonatomic, strong, readonly) CALayerSpringParametersOverrideParameters * _Nullable parameters;
|
||||
|
|
|
|||
|
|
@ -115,18 +115,42 @@ static bool notyfyingShiftState = false;
|
|||
|
||||
@implementation CALayerSpringParametersOverrideParameters
|
||||
|
||||
- (instancetype _Nonnull)initWithStiffness:(CGFloat)stiffness damping:(CGFloat)damping duration:(double)duration; {
|
||||
- (instancetype _Nonnull)init {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_stiffness = stiffness;
|
||||
_damping = damping;
|
||||
_duration = duration;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CALayerSpringParametersOverrideParametersSpring
|
||||
|
||||
- (instancetype _Nonnull)initWithStiffness:(CGFloat)stiffness damping:(CGFloat)damping duration:(double)duration {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_stiffness = stiffness;
|
||||
_damping = damping;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CALayerSpringParametersOverrideParametersCustomCurve
|
||||
|
||||
- (instancetype _Nonnull)initWithCp1:(CGPoint)cp1 cp2:(CGPoint)cp2 {
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
_cp1 = cp1;
|
||||
_cp2 = cp2;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CALayerSpringParametersOverride
|
||||
|
||||
- (instancetype _Nonnull)initWithParameters:(CALayerSpringParametersOverrideParameters * _Nullable)parameters {
|
||||
|
|
@ -167,12 +191,13 @@ static NSMutableArray<CALayerSpringParametersOverride *> *currentSpringParameter
|
|||
if (currentSpringParametersOverrideStack().count != 0 && [anim isKindOfClass:[CASpringAnimation class]]) {
|
||||
CALayerSpringParametersOverride *overrideData = [currentSpringParametersOverrideStack() lastObject];
|
||||
if (overrideData) {
|
||||
if (overrideData.parameters) {
|
||||
if ([overrideData.parameters isKindOfClass:[CALayerSpringParametersOverrideParametersSpring class]]) {
|
||||
CALayerSpringParametersOverrideParametersSpring *parameters = (CALayerSpringParametersOverrideParametersSpring *)overrideData.parameters;
|
||||
CABasicAnimation *sourceAnimation = (CABasicAnimation *)anim;
|
||||
|
||||
CASpringAnimation *animation = makeSpringBounceAnimationImpl(sourceAnimation.keyPath, 0.0, overrideData.parameters.damping);
|
||||
CASpringAnimation *animation = makeSpringBounceAnimationImpl(sourceAnimation.keyPath, 0.0, parameters.damping);
|
||||
|
||||
animation.stiffness = overrideData.parameters.stiffness;
|
||||
animation.stiffness = parameters.stiffness;
|
||||
animation.fromValue = sourceAnimation.fromValue;
|
||||
animation.toValue = sourceAnimation.toValue;
|
||||
animation.byValue = sourceAnimation.byValue;
|
||||
|
|
@ -189,7 +214,34 @@ static NSMutableArray<CALayerSpringParametersOverride *> *currentSpringParameter
|
|||
if (k != 0.0 && k != 1.0) {
|
||||
speed = 1.0f / k;
|
||||
}
|
||||
animation.speed = sourceAnimation.speed * (float)(animation.duration / overrideData.parameters.duration);
|
||||
animation.speed = sourceAnimation.speed * (float)(animation.duration / parameters.duration);
|
||||
|
||||
updatedAnimation = animation;
|
||||
} else if ([overrideData.parameters isKindOfClass:[CALayerSpringParametersOverrideParametersCustomCurve class]]) {
|
||||
CALayerSpringParametersOverrideParametersCustomCurve *parameters = (CALayerSpringParametersOverrideParametersCustomCurve *)overrideData.parameters;
|
||||
CABasicAnimation *sourceAnimation = (CABasicAnimation *)anim;
|
||||
|
||||
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:sourceAnimation.keyPath];
|
||||
animation.fromValue = sourceAnimation.fromValue;
|
||||
animation.toValue = sourceAnimation.toValue;
|
||||
animation.byValue = sourceAnimation.byValue;
|
||||
animation.additive = sourceAnimation.additive;
|
||||
animation.duration = sourceAnimation.duration;
|
||||
animation.timingFunction = [[CAMediaTimingFunction alloc] initWithControlPoints:parameters.cp1.x :parameters.cp1.y :parameters.cp2.x :parameters.cp2.y];
|
||||
animation.removedOnCompletion = sourceAnimation.isRemovedOnCompletion;
|
||||
animation.fillMode = sourceAnimation.fillMode;
|
||||
animation.speed = sourceAnimation.speed;
|
||||
animation.beginTime = sourceAnimation.beginTime;
|
||||
animation.timeOffset = sourceAnimation.timeOffset;
|
||||
animation.repeatCount = sourceAnimation.repeatCount;
|
||||
animation.autoreverses = sourceAnimation.autoreverses;
|
||||
|
||||
float k = animationDurationFactorImpl();
|
||||
float speed = 1.0f;
|
||||
if (k != 0.0 && k != 1.0) {
|
||||
speed = 1.0f / k;
|
||||
}
|
||||
animation.speed = speed * sourceAnimation.speed;
|
||||
|
||||
updatedAnimation = animation;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -846,7 +846,14 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||
if let peer {
|
||||
return .single(peer)
|
||||
} else {
|
||||
return context.engine.peers.findChannelById(channelId: monoforumId.id._internalGetInt64Value())
|
||||
return context.engine.peers.fetchAndUpdateCachedPeerData(peerId: channel.id)
|
||||
|> mapToSignal { result -> Signal<EnginePeer?, NoError> in
|
||||
if result {
|
||||
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: monoforumId))
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|> map { peer -> ResolveInternalUrlResult in
|
||||
|
|
@ -1209,8 +1216,16 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
|
|||
case let .collectible(slug):
|
||||
return .single(.progress) |> then(context.engine.payments.getUniqueStarGift(slug: slug)
|
||||
|> map { gift -> ResolveInternalUrlResult in
|
||||
return .result(.collectible(gift: gift))
|
||||
return .result(.collectible(.gift(gift)))
|
||||
})
|
||||
|> `catch` { error -> Signal<ResolveInternalUrlResult, NoError> in
|
||||
switch error {
|
||||
case .alreadyBurned:
|
||||
return .single(.result(.collectible(.alreadyBurned)))
|
||||
default:
|
||||
return .single(.result(.collectible(.invalidSlug)))
|
||||
}
|
||||
}
|
||||
case let .auction(slug):
|
||||
if let giftAuctionsManager = context.giftAuctionsManager {
|
||||
return .single(.progress) |> then(giftAuctionsManager.auctionContext(for: .slug(slug))
|
||||
|
|
|
|||
|
|
@ -82,10 +82,12 @@ class WebSearchGalleryController: ViewController {
|
|||
private var centralEntryIndex: Int?
|
||||
|
||||
private let centralItemTitle = Promise<String>()
|
||||
private let centralItemTitleView = Promise<UIView?>()
|
||||
private let centralItemTitleContent = Promise<GalleryTitleView.Content?>()
|
||||
private let centralItemNavigationStyle = Promise<GalleryItemNodeNavigationStyle>()
|
||||
private let centralItemFooterContentNode = Promise<(GalleryFooterContentNode?, GalleryOverlayContentNode?)>()
|
||||
private let centralItemAttributesDisposable = DisposableSet();
|
||||
private let centralItemAttributesDisposable = DisposableSet()
|
||||
|
||||
private let titleView: GalleryTitleView
|
||||
|
||||
private let checkedDisposable = MetaDisposable()
|
||||
private var checkNode: GalleryNavigationCheckNode?
|
||||
|
|
@ -105,6 +107,8 @@ class WebSearchGalleryController: ViewController {
|
|||
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.titleView = GalleryTitleView(context: context, presentationData: self.presentationData)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: WebSearchGalleryController.navigationTheme, strings: NavigationBarStrings(presentationStrings: self.presentationData.strings)))
|
||||
|
||||
self.controllerInteraction = WebSearchGalleryControllerInteraction(dismiss: { [weak self] animated in
|
||||
|
|
@ -151,8 +155,8 @@ class WebSearchGalleryController: ViewController {
|
|||
self?.navigationItem.title = title
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(self.centralItemTitleView.get().start(next: { [weak self] titleView in
|
||||
self?.navigationItem.titleView = titleView
|
||||
self.centralItemAttributesDisposable.add(self.centralItemTitleContent.get().start(next: { [weak self] titleContent in
|
||||
self?.titleView.setContent(content: titleContent)
|
||||
}))
|
||||
|
||||
self.centralItemAttributesDisposable.add(self.centralItemFooterContentNode.get().start(next: { [weak self] footerContentNode, _ in
|
||||
|
|
@ -232,7 +236,7 @@ class WebSearchGalleryController: ViewController {
|
|||
}, currentItemNode: { [weak self] in
|
||||
return self?.galleryNode.pager.centralItemNode()
|
||||
})
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction)
|
||||
self.displayNode = GalleryControllerNode(context: self.context, controllerInteraction: controllerInteraction, titleView: self.titleView)
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self.galleryNode.statusBar = self.statusBar
|
||||
|
|
@ -265,7 +269,7 @@ class WebSearchGalleryController: ViewController {
|
|||
|
||||
if let node = strongSelf.galleryNode.pager.centralItemNode() {
|
||||
strongSelf.centralItemTitle.set(node.title())
|
||||
strongSelf.centralItemTitleView.set(node.titleView())
|
||||
strongSelf.centralItemTitleContent.set(node.titleContent())
|
||||
strongSelf.centralItemNavigationStyle.set(node.navigationStyle())
|
||||
strongSelf.centralItemFooterContentNode.set(node.footerContent())
|
||||
}
|
||||
|
|
@ -317,7 +321,7 @@ class WebSearchGalleryController: ViewController {
|
|||
|
||||
if let centralItemNode = self.galleryNode.pager.centralItemNode(), let presentationArguments = self.presentationArguments as? WebSearchGalleryControllerPresentationArguments {
|
||||
self.centralItemTitle.set(centralItemNode.title())
|
||||
self.centralItemTitleView.set(centralItemNode.titleView())
|
||||
self.centralItemTitleContent.set(centralItemNode.titleContent())
|
||||
self.centralItemNavigationStyle.set(centralItemNode.navigationStyle())
|
||||
self.centralItemFooterContentNode.set(centralItemNode.footerContent())
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ final class WebSearchGalleryFooterContentNode: GalleryFooterContentNode {
|
|||
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat {
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, leftInset: CGFloat, rightInset: CGFloat, bottomInset: CGFloat, contentInset: CGFloat, transition: ContainedViewLayoutTransition) -> LayoutInfo {
|
||||
let width = size.width
|
||||
let panelSize: CGFloat = 49.0
|
||||
var panelHeight: CGFloat = panelSize + bottomInset
|
||||
|
|
@ -52,7 +52,7 @@ final class WebSearchGalleryFooterContentNode: GalleryFooterContentNode {
|
|||
self.cancelButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - panelSize), size: CGSize(width: panelSize, height: panelSize))
|
||||
self.sendButton.frame = CGRect(origin: CGPoint(x: width - panelSize - rightInset, y: panelHeight - bottomInset - panelSize), size: CGSize(width: panelSize, height: panelSize))
|
||||
|
||||
return panelHeight
|
||||
return LayoutInfo(height: panelHeight, needsShadow: false)
|
||||
}
|
||||
|
||||
override func animateIn(fromHeight: CGFloat, previousContentNode: GalleryFooterContentNode, transition: ContainedViewLayoutTransition) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue