chore: migrate to new version + fixed several critical bugs
- Migrated project to latest Telegram iOS base (v12.3.2+) - Fixed circular dependency between GhostModeManager and MiscSettingsManager - Fixed multiple Bazel build configuration errors (select() default conditions) - Fixed duplicate type definitions in PeerInfoScreen - Fixed swiftmodule directory resolution in build scripts - Added Ghostgram Settings tab in main Settings menu with all 5 features - Cleared sensitive credentials from config.json (template-only now) - Excluded bazel-cache from version control
@@ -22,9 +22,20 @@ build --spawn_strategy=standalone
|
||||
build --strategy=SwiftCompile=standalone
|
||||
build --define RULES_SWIFT_BUILD_DUMMY_WORKER=1
|
||||
|
||||
build:swift_profile --jobs=1
|
||||
build:swift_profile --local_cpu_resources=1
|
||||
build:swift_profile --features=-swift.enable_batch_mode
|
||||
build:swift_profile --experimental_ui_max_stdouterr_bytes=104857600
|
||||
build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend
|
||||
build:swift_profile --@build_bazel_rules_swift//swift:copt=-warn-long-function-bodies=350
|
||||
build:swift_profile --@build_bazel_rules_swift//swift:copt=-Xfrontend
|
||||
build:swift_profile --@build_bazel_rules_swift//swift:copt=-warn-long-expression-type-checking=350
|
||||
|
||||
common:index_build --experimental_convenience_symlinks=ignore
|
||||
common:index_build --bes_backend= --bes_results_url=
|
||||
common:index_build --nolegacy_important_outputs
|
||||
common:index_build --show_result=0
|
||||
common:index_build --define=buildNumber=10000
|
||||
common:index_build --define=telegramVersion=12.2.2
|
||||
|
||||
common --registry=https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/
|
||||
|
||||
@@ -59,8 +59,6 @@ bazel-telegram-ios
|
||||
bazel-telegram-ios/*
|
||||
bazel-testlogs
|
||||
bazel-testlogs/*
|
||||
bazel-telegram-antidelete
|
||||
bazel-telegram-antidelete/*
|
||||
xcodeproj.bazelrc
|
||||
*/*.swp
|
||||
*.swp
|
||||
@@ -76,10 +74,7 @@ buildServer.json
|
||||
Telegram.LSP.json
|
||||
**/.build/**
|
||||
spm-files
|
||||
xcode-files
|
||||
.bsp/**
|
||||
|
||||
*.mobileprovision
|
||||
build-system/fake-codesigning/profiles/*
|
||||
|
||||
*.backup
|
||||
*_BACKUP*
|
||||
/.claude/
|
||||
/buildbox/*
|
||||
|
||||
@@ -50,6 +50,11 @@ internal_testflight:
|
||||
- python3 -u build-system/Make/Make.py remote-deploy-testflight --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
|
||||
environment:
|
||||
name: testflight_llc
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- build/artifacts
|
||||
expire_in: 1 week
|
||||
|
||||
appstore_development:
|
||||
tags:
|
||||
@@ -80,7 +85,7 @@ experimental_i:
|
||||
- export PATH=/opt/homebrew/opt/ruby/bin:$PATH
|
||||
- export PATH=`gem environment gemdir`/bin:$PATH
|
||||
- python3 -u build-system/Make/Make.py remote-build --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --cacheHost="$TELEGRAM_BAZEL_CACHE_HOST" --configurationPath="build-system/appcenter-configuration.json" --gitCodesigningRepository="$TELEGRAM_GIT_CODESIGNING_REPOSITORY" --gitCodesigningType=adhoc --configuration=release_arm64
|
||||
- python3 -u build-system/Make/DeployToFirebase.py --configuration="$TELEGRAM_PRIVATE_DATA_PATH/firebase-configurations/firebase-internal.json" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
|
||||
- python3 -u build-system/Make/Make.py remote-deploy-testflight --darwinContainers="$DARWIN_CONTAINERS" --darwinContainersHost="$DARWIN_CONTAINERS_HOST" --ipa="build/artifacts/Telegram.ipa" --dsyms="build/artifacts/Telegram.DSYMs.zip"
|
||||
environment:
|
||||
name: experimental
|
||||
artifacts:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"backgroundIndexing": true,
|
||||
"backgroundPreparationMode": "build",
|
||||
"buildServerWorkspaceRequestsTimeout": 999,
|
||||
"buildSettingsTimeout": 999,
|
||||
"defaultWorkspaceType": "buildServer",
|
||||
"logging": {
|
||||
"level": "error",
|
||||
|
||||
@@ -2,7 +2,7 @@ load("@sourcekit_bazel_bsp//rules:setup_sourcekit_bsp.bzl", "setup_sourcekit_bsp
|
||||
|
||||
setup_sourcekit_bsp(
|
||||
name = "setup_sourcekit_bsp_telegram_project",
|
||||
bazel_wrapper = "./build-input/bazel-8.4.2-darwin-arm64",
|
||||
bazel_wrapper = "./build-input/bazel-8.4.2-darwin-x86_64",
|
||||
files_to_watch = [
|
||||
"**/*.swift",
|
||||
],
|
||||
|
||||
@@ -1,156 +1,156 @@
|
||||
{
|
||||
"lockFileVersion": 18,
|
||||
"registryFileHashes": {
|
||||
"https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20230802.0/MODULE.bazel": "d253ae36a8bd9ee3c5955384096ccb6baf16a1b1e93e858370da0a3b94f77c16",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20230802.1/MODULE.bazel": "fa92e2eb41a04df73cdabeec37107316f7e5272650f81d6cc096418fe647b915",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20240116.1/MODULE.bazel": "37bcdb4440fbb61df6a1c296ae01b327f19e9bb521f9b8e26ec854b6f97309ed",
|
||||
"https://bcr.bazel.build/modules/abseil-cpp/20240116.1/source.json": "9be551b8d4e3ef76875c0d744b5d6a504a27e3ae67bc6b28f46415fd2d2957da",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b",
|
||||
"https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.6.1/MODULE.bazel": "8fdee2dbaace6c252131c00e1de4b165dc65af02ea278476187765e1a617b917",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.7.0/MODULE.bazel": "0db596f4563de7938de764cc8deeabec291f55e8ec15299718b93c4423e9796d",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/MODULE.bazel": "3120d80c5861aa616222ec015332e5f8d3171e062e3e804a2a0253e1be26e59b",
|
||||
"https://bcr.bazel.build/modules/bazel_skylib/1.7.1/source.json": "f121b43eeefc7c29efbd51b83d08631e2347297c95aac9764a701f2a6a2bb953",
|
||||
"https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
|
||||
"https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
|
||||
"https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
|
||||
"https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
|
||||
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
|
||||
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4",
|
||||
"https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
|
||||
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
|
||||
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
|
||||
"https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
|
||||
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/MODULE.bazel": "6f7b417dcc794d9add9e556673ad25cb3ba835224290f4f848f8e2db1e1fca74",
|
||||
"https://bcr.bazel.build/modules/nlohmann_json/3.6.1/source.json": "f448c6e8963fdfa7eb831457df83ad63d3d6355018f6574fb017e8169deb43a9",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.11/MODULE.bazel": "0daefc49732e227caa8bfa834d65dc52e8cc18a2faf80df25e8caea151a9413f",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.11/source.json": "f7e188b79ebedebfe75e9e1d098b8845226c7992b307e28e1496f23112e8fc29",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
|
||||
"https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
|
||||
"https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
|
||||
"https://bcr.bazel.build/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
|
||||
"https://bcr.bazel.build/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
|
||||
"https://bcr.bazel.build/modules/protobuf/29.0-rc2/MODULE.bazel": "6241d35983510143049943fc0d57937937122baf1b287862f9dc8590fc4c37df",
|
||||
"https://bcr.bazel.build/modules/protobuf/29.0-rc3/MODULE.bazel": "33c2dfa286578573afc55a7acaea3cada4122b9631007c594bf0729f41c8de92",
|
||||
"https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
|
||||
"https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981",
|
||||
"https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
|
||||
"https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
|
||||
"https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022",
|
||||
"https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206",
|
||||
"https://bcr.bazel.build/modules/re2/2023-09-01/source.json": "e044ce89c2883cd957a2969a43e79f7752f9656f6b20050b62f90ede21ec6eb4",
|
||||
"https://bcr.bazel.build/modules/rules_android/0.1.1/MODULE.bazel": "48809ab0091b07ad0182defb787c4c5328bd3a278938415c00a7b69b50c4d3a8",
|
||||
"https://bcr.bazel.build/modules/rules_android/0.1.1/source.json": "e6986b41626ee10bdc864937ffb6d6bf275bb5b9c65120e6137d56e6331f089e",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.1/MODULE.bazel": "cb2aa0747f84c6c3a78dad4e2049c154f08ab9d166b1273835a8174940365647",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.14/MODULE.bazel": "5e343a3aac88b8d7af3b1b6d2093b55c347b8eefc2e7d1442f7a02dc8fea48ac",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.15/MODULE.bazel": "6704c35f7b4a72502ee81f61bf88706b54f06b3cbe5558ac17e2e14666cd5dcc",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.16/MODULE.bazel": "7661303b8fc1b4d7f532e54e9d6565771fea666fbdf839e0a86affcd02defe87",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.2/MODULE.bazel": "6915987c90970493ab97393024c156ea8fb9f3bea953b2f3ec05c34f19b5695c",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.6/MODULE.bazel": "abf360251023dfe3efcef65ab9d56beefa8394d4176dd29529750e1c57eaa33f",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.2/MODULE.bazel": "557ddc3a96858ec0d465a87c0a931054d7dcfd6583af2c7ed3baf494407fd8d0",
|
||||
"https://bcr.bazel.build/modules/rules_cc/0.1.2/source.json": "53fcb09b5816c83ca60d9d7493faf3bfaf410dfc2f15deb52d6ddd146b8d43f0",
|
||||
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
|
||||
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
|
||||
"https://bcr.bazel.build/modules/rules_java/4.0.0/MODULE.bazel": "5a78a7ae82cd1a33cef56dc578c7d2a46ed0dca12643ee45edbb8417899e6f74",
|
||||
"https://bcr.bazel.build/modules/rules_java/5.3.5/MODULE.bazel": "a4ec4f2db570171e3e5eb753276ee4b389bae16b96207e9d3230895c99644b86",
|
||||
"https://bcr.bazel.build/modules/rules_java/6.0.0/MODULE.bazel": "8a43b7df601a7ec1af61d79345c17b31ea1fedc6711fd4abfd013ea612978e39",
|
||||
"https://bcr.bazel.build/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6",
|
||||
"https://bcr.bazel.build/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
|
||||
"https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017",
|
||||
"https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel": "a56b85e418c83eb1839819f0b515c431010160383306d13ec21959ac412d2fe7",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/5.1/MODULE.bazel": "33f6f999e03183f7d088c9be518a63467dfd0be94a11d0055fe2d210f89aa909",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/5.2/MODULE.bazel": "d9351ba35217ad0de03816ef3ed63f89d411349353077348a45348b096615036",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/5.3/MODULE.bazel": "bf93870767689637164657731849fb887ad086739bd5d360d90007a581d5527d",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/6.1/MODULE.bazel": "75b5fec090dbd46cf9b7d8ea08cf84a0472d92ba3585b476f44c326eda8059c4",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/6.3/MODULE.bazel": "c998e060b85f71e00de5ec552019347c8bca255062c990ac02d051bb80a38df0",
|
||||
"https://bcr.bazel.build/modules/rules_jvm_external/6.3/source.json": "6f5f5a5a4419ae4e37c35a5bb0a6ae657ed40b7abc5a5189111b47fcebe43197",
|
||||
"https://bcr.bazel.build/modules/rules_kotlin/1.9.0/MODULE.bazel": "ef85697305025e5a61f395d4eaede272a5393cee479ace6686dba707de804d59",
|
||||
"https://bcr.bazel.build/modules/rules_kotlin/1.9.6/MODULE.bazel": "d269a01a18ee74d0335450b10f62c9ed81f2321d7958a2934e44272fe82dcef3",
|
||||
"https://bcr.bazel.build/modules/rules_kotlin/1.9.6/source.json": "2faa4794364282db7c06600b7e5e34867a564ae91bda7cae7c29c64e9466b7d5",
|
||||
"https://bcr.bazel.build/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
|
||||
"https://bcr.bazel.build/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
|
||||
"https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
|
||||
"https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
|
||||
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
|
||||
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
|
||||
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/source.json": "bd82e5d7b9ce2d31e380dd9f50c111d678c3bdaca190cb76b0e1c71b05e1ba8a",
|
||||
"https://bcr.bazel.build/modules/rules_proto/4.0.0/MODULE.bazel": "a7a7b6ce9bee418c1a760b3d84f83a299ad6952f9903c67f19e4edd964894e06",
|
||||
"https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
|
||||
"https://bcr.bazel.build/modules/rules_proto/6.0.2/MODULE.bazel": "ce916b775a62b90b61888052a416ccdda405212b6aaeb39522f7dc53431a5e73",
|
||||
"https://bcr.bazel.build/modules/rules_proto/7.0.2/MODULE.bazel": "bf81793bd6d2ad89a37a40693e56c61b0ee30f7a7fdbaf3eabbf5f39de47dea2",
|
||||
"https://bcr.bazel.build/modules/rules_proto/7.0.2/source.json": "1e5e7260ae32ef4f2b52fd1d0de8d03b606a44c91b694d2f1afb1d3b28a48ce1",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.10.2/MODULE.bazel": "cc82bc96f2997baa545ab3ce73f196d040ffb8756fd2d66125a530031cd90e5f",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.23.1/MODULE.bazel": "49ffccf0511cb8414de28321f5fcf2a31312b47c40cc21577144b7447f2bf300",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.25.0/MODULE.bazel": "72f1506841c920a1afec76975b35312410eea3aa7b63267436bfb1dd91d2d382",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.27.1/MODULE.bazel": "65dc875cc1a06c30d5bbdba7ab021fd9e551a6579e408a3943a61303e2228a53",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.28.0/MODULE.bazel": "cba2573d870babc976664a912539b320cbaa7114cd3e8f053c720171cde331ed",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.31.0/MODULE.bazel": "93a43dc47ee570e6ec9f5779b2e64c1476a6ce921c48cc9a1678a91dd5f8fd58",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.4.0/MODULE.bazel": "9208ee05fd48bf09ac60ed269791cf17fb343db56c8226a720fbb1cdf467166c",
|
||||
"https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.3.0/MODULE.bazel": "8361d57eafb67c09b75bf4bbe6be360e1b8f4f18118ab48037f2bd50aa2ccb13",
|
||||
"https://bcr.bazel.build/modules/rules_python/1.3.0/source.json": "25932f917cd279c7baefa6cb1d3fa8750a7a29de522024449b19af6eab51f4a0",
|
||||
"https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
|
||||
"https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
|
||||
"https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
|
||||
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
|
||||
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/MODULE.bazel": "5e463fbfba7b1701d957555ed45097d7f984211330106ccd1352c6e0af0dcf91",
|
||||
"https://bcr.bazel.build/modules/swift_argument_parser/1.3.1.1/source.json": "32bd87e5f4d7acc57c5b2ff7c325ae3061d5e242c0c4c214ae87e0f1c13e54cb",
|
||||
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
|
||||
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806",
|
||||
"https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230125.1/MODULE.bazel": "89047429cb0207707b2dface14ba7f8df85273d484c2572755be4bab7ce9c3a0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/abseil-cpp/20230802.0.bcr.1/MODULE.bazel": "1c8cec495288dccd14fdae6e3f95f772c1c91857047a098fad772034264cc8cb",
|
||||
"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/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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.17.0/MODULE.bazel": "039de32d21b816b47bd42c778e0454217e9c9caac4a3cf8e15c7231ee3ddee4d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65",
|
||||
"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.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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
|
||||
"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/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/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/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/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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/27.0/MODULE.bazel": "7873b60be88844a0a1d8f80b9d5d20cfbd8495a689b8763e76c6372998d3f64c",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/protobuf/27.1/MODULE.bazel": "703a7b614728bb06647f965264967a8ef1c39e09e8f167b3ca0bb1fd80449c0d",
|
||||
"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/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
|
||||
"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/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/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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.10/MODULE.bazel": "ec1705118f7eaedd6e118508d3d26deba2a4e76476ada7e0e3965211be012002",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_cc/0.0.13/MODULE.bazel": "0e8529ed7b323dad0775ff924d2ae5af7640b23553dfcd4d34344c7e7a867191",
|
||||
"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.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_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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/6.4.0/MODULE.bazel": "e986a9fe25aeaa84ac17ca093ef13a4637f6107375f64667a15999f77db6c8f6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/6.5.2/MODULE.bazel": "1d440d262d0e08453fa0c4d8f699ba81609ed0e9a9a0f02cd10b3e7942e61e31",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.10.0/MODULE.bazel": "530c3beb3067e870561739f1144329a21c851ff771cd752a49e06e3dc9c2e71a",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.12.2/MODULE.bazel": "579c505165ee757a4280ef83cda0150eea193eed3bef50b1004ba88b99da6de6",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615",
|
||||
"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_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_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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/0.0.3/MODULE.bazel": "627e9ab0247f7d1e05736b59dbb1b6871373de5ad31c3011880b4133cafd4bd0",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/0.0.7/MODULE.bazel": "088fbeb0b6a419005b89cf93fe62d9517c0a2b8bb56af3244af65ecfe37e7d5d",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
|
||||
"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.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_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.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_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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/stardoc/0.7.0/MODULE.bazel": "05e3d6d30c099b6770e97da986c53bd31844d7f13d41412480ea265ac9e8079c",
|
||||
"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/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",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806",
|
||||
"https://raw.githubusercontent.com/bazelbuild/bazel-central-registry/main/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
|
||||
},
|
||||
"selectedYankedVersions": {},
|
||||
"moduleExtensions": {
|
||||
|
||||
@@ -1,72 +1,116 @@
|
||||
# 👻 Ghostgram iOS
|
||||
# Telegram iOS Source Code Compilation Guide
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/TelegramMessenger/Telegram-iOS/master/Telegram/Telegram-iOS/Images.xcassets/AppIcon.appiconset/Icon-60%403v.png" width="128" height="128" alt="Ghostgram Logo">
|
||||
</p>
|
||||
We welcome all developers to use our API and source code to create applications on our platform.
|
||||
There are several things we require from **all developers** for the moment.
|
||||
|
||||
<p align="center">
|
||||
<b>The ultimate Telegram fork for privacy, control, and freedom.</b>
|
||||
</p>
|
||||
# Creating your Telegram Application
|
||||
|
||||
<p align="center">
|
||||
<a href="https://t.me/ceopoco"><img src="https://img.shields.io/badge/Telegram-Support-blue.svg?logo=telegram" alt="Telegram Support"></a>
|
||||
<img src="https://img.shields.io/badge/Platform-iOS-lightgrey.svg" alt="Platform iOS">
|
||||
<img src="https://img.shields.io/badge/License-GPLv2-green.svg" alt="License GPLv2">
|
||||
</p>
|
||||
1. [**Obtain your own api_id**](https://core.telegram.org/api/obtaining_api_id) for your application.
|
||||
2. Please **do not** use the name Telegram for your app — or make sure your users understand that it is unofficial.
|
||||
3. Kindly **do not** use our standard logo (white paper plane in a blue circle) as your app's logo.
|
||||
3. Please study our [**security guidelines**](https://core.telegram.org/mtproto/security_guidelines) and take good care of your users' data and privacy.
|
||||
4. Please remember to publish **your** code too in order to comply with the licences.
|
||||
|
||||
---
|
||||
# Quick Compilation Guide
|
||||
|
||||
## 🌟 Key Features
|
||||
## Get the Code
|
||||
|
||||
### 🛡️ Anti-Delete System
|
||||
*Never miss a message again. Ghostgram keeps what others try to hide.*
|
||||
- **Local Archive:** Automatically saves messages deleted by the sender for everyone.
|
||||
- **Media Preservation:** Photos and videos from deleted messages are archived locally.
|
||||
- **Edit History:** View original content of edited messages.
|
||||
- **Visual Indicators:** Deleted messages are clearly marked with a 🗑️ icon.
|
||||
```
|
||||
git clone --recursive -j8 https://github.com/TelegramMessenger/Telegram-iOS.git
|
||||
```
|
||||
|
||||
### 👤 Ghost Mode
|
||||
- **Invisible Reading:** Read messages without sending "Read" receipts (single checkmark).
|
||||
- **Stealth Stories:** View stories anonymously without appearing in the viewer list.
|
||||
- **Hidden Input:** Hide "Typing..." status from your contacts.
|
||||
- **Online Privacy:** Hide your online status or use **Force Offline** to always appear as "last seen recently".
|
||||
## Setup Xcode
|
||||
|
||||
### 🎙️ Advanced Audio Tools
|
||||
- **Free Transcription:** Get text from voice and video messages without Telegram Premium (On-device processing).
|
||||
- **Voice Morpher:** Real-time voice changer for your outgoing audio messages.
|
||||
Install Xcode (directly from https://developer.apple.com/download/applications or using the App Store).
|
||||
|
||||
### 🔓 Bypass Limits
|
||||
- **Copy Protection Bypass:** Forward or save content from channels that have restricted it.
|
||||
- **Infinite View-Once:** View-once media stays available as long as you need.
|
||||
- **Secret Screenshots:** Take screenshots in secret chats without notification.
|
||||
## Adjust Configuration
|
||||
|
||||
### 🛠️ More Features
|
||||
- **Device Spoofing:** Change how your device appears to others.
|
||||
- **Ad Blocker:** Say goodbye to sponsored messages in channels.
|
||||
- **Always Online:** Keep your status active even when the app is in the background.
|
||||
1. Generate a random identifier:
|
||||
```
|
||||
openssl rand -hex 8
|
||||
```
|
||||
2. Create a new Xcode project. Use `Telegram` as the Product Name. Use `org.{identifier from step 1}` as the Organization Identifier.
|
||||
3. Open `Keychain Access` and navigate to `Certificates`. Locate `Apple Development: your@email.address (XXXXXXXXXX)` and double tap the certificate. Under `Details`, locate `Organizational Unit`. This is the Team ID.
|
||||
4. Edit `build-system/template_minimal_development_configuration.json`. Use data from the previous steps.
|
||||
|
||||
---
|
||||
## Generate an Xcode project
|
||||
|
||||
## 🛠 Installation & Building
|
||||
```
|
||||
python3 build-system/Make/Make.py \
|
||||
--cacheDir="$HOME/telegram-bazel-cache" \
|
||||
generateProject \
|
||||
--configurationPath=build-system/template_minimal_development_configuration.json \
|
||||
--xcodeManagedCodesigning
|
||||
```
|
||||
|
||||
Ghostgram is a developer-centric fork. To build it from source, follow our detailed guide:
|
||||
# Advanced Compilation Guide
|
||||
|
||||
📖 **[Build & Compilation Guide](BUILD.md)**
|
||||
## Xcode
|
||||
|
||||
---
|
||||
1. Copy and edit `build-system/appstore-configuration.json`.
|
||||
2. Copy `build-system/fake-codesigning`. Create and download provisioning profiles, using the `profiles` folder as a reference for the entitlements.
|
||||
3. Generate an Xcode project:
|
||||
```
|
||||
python3 build-system/Make/Make.py \
|
||||
--cacheDir="$HOME/telegram-bazel-cache" \
|
||||
generateProject \
|
||||
--configurationPath=configuration_from_step_1.json \
|
||||
--codesigningInformationPath=directory_from_step_2
|
||||
```
|
||||
|
||||
## ⚠️ Disclaimer & Legal
|
||||
## IPA
|
||||
|
||||
> **Educational Use Only:** This is a modified version of the Telegram client. Use it at your own risk. The developer is not responsible for any account bans or data loss.
|
||||
1. Repeat the steps from the previous section. Use distribution provisioning profiles.
|
||||
2. Run:
|
||||
```
|
||||
python3 build-system/Make/Make.py \
|
||||
--cacheDir="$HOME/telegram-bazel-cache" \
|
||||
build \
|
||||
--configurationPath=...see previous section... \
|
||||
--codesigningInformationPath=...see previous section... \
|
||||
--buildNumber=100001 \
|
||||
--configuration=release_arm64
|
||||
```
|
||||
|
||||
- **Unofficial:** This project is not affiliated with, endorsed by, or in any way officially connected with Telegram Messenger Inc.
|
||||
- **Terms of Service:** Using third-party clients may violate Telegram's ToS.
|
||||
- **License:** Based on [Telegram-iOS](https://github.com/TelegramMessenger/Telegram-iOS), licensed under **GPLv2**.
|
||||
# FAQ
|
||||
|
||||
---
|
||||
## Xcode is stuck at "build-request.json not updated yet"
|
||||
|
||||
### 👨💻 Support & Community
|
||||
Report issues or join the discussion:
|
||||
- **Telegram:** [@ceopoco](https://t.me/ceosppw)
|
||||
Occasionally, you might observe the following message in your build log:
|
||||
```
|
||||
"/Users/xxx/Library/Developer/Xcode/DerivedData/Telegram-xxx/Build/Intermediates.noindex/XCBuildData/xxx.xcbuilddata/build-request.json" not updated yet, waiting...
|
||||
```
|
||||
|
||||
**Made with 🖤 for the community.**
|
||||
Should this occur, simply cancel the ongoing build and initiate a new one.
|
||||
|
||||
## Telegram_xcodeproj: no such package
|
||||
|
||||
Following a system restart, the auto-generated Xcode project might encounter a build failure accompanied by this error:
|
||||
```
|
||||
ERROR: Skipping '@rules_xcodeproj_generated//generator/Telegram/Telegram_xcodeproj:Telegram_xcodeproj': no such package '@rules_xcodeproj_generated//generator/Telegram/Telegram_xcodeproj': BUILD file not found in directory 'generator/Telegram/Telegram_xcodeproj' of external repository @rules_xcodeproj_generated. Add a BUILD file to a directory to mark it as a package.
|
||||
```
|
||||
|
||||
If you encounter this issue, re-run the project generation steps in the README.
|
||||
|
||||
|
||||
# Tips
|
||||
|
||||
## Codesigning is not required for simulator-only builds
|
||||
|
||||
Add `--disableProvisioningProfiles`:
|
||||
```
|
||||
python3 build-system/Make/Make.py \
|
||||
--cacheDir="$HOME/telegram-bazel-cache" \
|
||||
generateProject \
|
||||
--configurationPath=path-to-configuration.json \
|
||||
--codesigningInformationPath=path-to-provisioning-data \
|
||||
--disableProvisioningProfiles
|
||||
```
|
||||
|
||||
## Versions
|
||||
|
||||
Each release is built using a specific Xcode version (see `versions.json`). The helper script checks the versions of the installed software and reports an error if they don't match the ones specified in `versions.json`. It is possible to bypass these checks:
|
||||
|
||||
```
|
||||
python3 build-system/Make/Make.py --overrideXcodeVersion build ... # Don't check the version of Xcode
|
||||
```
|
||||
|
||||
@@ -8,8 +8,6 @@ load("@build_bazel_rules_apple//apple:ios.bzl",
|
||||
"ios_application",
|
||||
"ios_extension",
|
||||
"ios_framework",
|
||||
"ios_unit_test",
|
||||
"ios_ui_test",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_apple//apple:resources.bzl",
|
||||
@@ -22,7 +20,6 @@ load("@build_bazel_rules_swift//swift:swift.bzl",
|
||||
|
||||
load(
|
||||
"@rules_xcodeproj//xcodeproj:defs.bzl",
|
||||
"top_level_target",
|
||||
"top_level_targets",
|
||||
"xcodeproj",
|
||||
"xcode_provisioning_profile",
|
||||
@@ -338,6 +335,7 @@ filegroup(
|
||||
|
||||
objc_library(
|
||||
name = "Main",
|
||||
module_name = "Main",
|
||||
srcs = [
|
||||
"Telegram-iOS/main.m"
|
||||
],
|
||||
@@ -345,6 +343,7 @@ objc_library(
|
||||
|
||||
swift_library(
|
||||
name = "Lib",
|
||||
module_name = "Lib",
|
||||
srcs = glob([
|
||||
"Telegram-iOS/Application.swift",
|
||||
]),
|
||||
@@ -1810,7 +1809,7 @@ ios_application(
|
||||
#"//third-party/boringssl:ssl",
|
||||
#"//third-party/boringssl:crypto",
|
||||
#"//submodules/TelegramVoip",
|
||||
#"//third-party/recaptcha:RecaptchaEnterprise",
|
||||
#"//third-party/recaptcha:RecaptchaEnterpriseSDK",
|
||||
"//submodules/TelegramUI",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -5,4 +5,4 @@ APP_SPECIFIC_URL_SCHEME=tgapp
|
||||
GLOBAL_CONSTANTS = APP_CONFIG_IS_INTERNAL_BUILD=false APP_CONFIG_IS_APPSTORE_BUILD=true APP_CONFIG_APPSTORE_ID=686449807 APP_SPECIFIC_URL_SCHEME="\"$(APP_SPECIFIC_URL_SCHEME)\""
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(GLOBAL_CONSTANTS)
|
||||
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=YOUR_API_ID APP_CONFIG_API_HASH="\"YOUR_API_HASH\"" APP_CONFIG_HOCKEYAPP_ID="\"ad8831329ffc8f8aff9a2b0b86558b24\""
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=8 APP_CONFIG_API_HASH="\"7245de8e747a0d6fbe11f7cc14fcc0bb\"" APP_CONFIG_HOCKEYAPP_ID="\"ad8831329ffc8f8aff9a2b0b86558b24\""
|
||||
|
||||
@@ -5,4 +5,4 @@ APP_SPECIFIC_URL_SCHEME=tgfork
|
||||
GLOBAL_CONSTANTS = APP_CONFIG_IS_INTERNAL_BUILD=false APP_CONFIG_IS_APPSTORE_BUILD=true APP_CONFIG_APPSTORE_ID=0 APP_SPECIFIC_URL_SCHEME="\"$(APP_SPECIFIC_URL_SCHEME)\""
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) $(GLOBAL_CONSTANTS)
|
||||
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=YOUR_API_ID APP_CONFIG_API_HASH="\"YOUR_API_HASH\"" APP_CONFIG_HOCKEYAPP_ID="\"\""
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) APP_CONFIG_API_ID=8 APP_CONFIG_API_HASH="\"7245de8e747a0d6fbe11f7cc14fcc0bb\"" APP_CONFIG_HOCKEYAPP_ID="\"\""
|
||||
@@ -1,115 +1,116 @@
|
||||
{
|
||||
"images" : [
|
||||
"images": [
|
||||
{
|
||||
"filename" : "BlueNotificationIcon@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
"filename": "GhostIcon@40x40.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueNotificationIcon@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
"filename": "GhostIcon@60x60.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@58x58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
"filename": "GhostIcon@58x58.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@87x87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
"filename": "GhostIcon@87x87.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@80x80.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
"filename": "GhostIcon@80x80.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueIcon@2x-1.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
"filename": "GhostIcon@120x120.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueIcon@2x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
"filename": "GhostIcon@120x120.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "2x",
|
||||
"size": "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueIcon@3x.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
"filename": "GhostIcon@180x180.png",
|
||||
"idiom": "iphone",
|
||||
"scale": "3x",
|
||||
"size": "60x60"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueNotificationIcon.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
"filename": "GhostIcon@20x20.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueNotificationIcon@2x-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
"filename": "GhostIcon@40x40.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "20x20"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@29x29.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
"filename": "GhostIcon@29x29.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@58x58-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
"filename": "GhostIcon@58x58.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@40x40-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
"filename": "GhostIcon@40x40.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple@80x80-1.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
"filename": "GhostIcon@80x80.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
"filename": "GhostIcon@76x76.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "1x",
|
||||
"size": "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueIconIpad@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
"filename": "GhostIcon@152x152.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "76x76"
|
||||
},
|
||||
{
|
||||
"filename" : "BlueIconLargeIpad@2x.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
"filename": "GhostIcon@167x167.png",
|
||||
"idiom": "ipad",
|
||||
"scale": "2x",
|
||||
"size": "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"filename" : "Simple-iTunesArtwork.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
"filename": "GhostIcon@1024x1024.png",
|
||||
"idiom": "ios-marketing",
|
||||
"scale": "1x",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
"info": {
|
||||
"author": "xcode",
|
||||
"version": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 6.5 KiB |
@@ -110,17 +110,17 @@
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need this so that you can take and share photos and videos.</string>
|
||||
<key>NSContactsUsageDescription</key>
|
||||
<string>Telegram stores your contacts heavily encrypted in the cloud to let you connect with your friends across all your devices.</string>
|
||||
<string>Ghostgram stores your contacts heavily encrypted in the cloud to let you connect with your friends across all your devices.</string>
|
||||
<key>NSFaceIDUsageDescription</key>
|
||||
<string>You can use Face ID to unlock the app.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>When you send your location to your friends, Telegram needs access to show them a map. You also need this to send locations from an Apple Watch.</string>
|
||||
<string>When you send your location to your friends, Ghostgram needs access to show them a map. You also need this to send locations from an Apple Watch.</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>When you send your location to your friends, Telegram needs access to show them a map.</string>
|
||||
<string>When you send your location to your friends, Ghostgram needs access to show them a map.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>We need this so that you can record and share voice messages and videos with sound.</string>
|
||||
<key>NSMotionUsageDescription</key>
|
||||
<string>When you send your location to your friends, Telegram needs access to show them a map.</string>
|
||||
<string>When you send your location to your friends, Ghostgram needs access to show them a map.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>We need this so that you can share photos and videos from your photo library.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
@@ -188,7 +188,7 @@
|
||||
<string>public.data</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>Telegram iOS Color Theme File</string>
|
||||
<string>Ghostgram iOS Color Theme File</string>
|
||||
<key>UTTypeIconFiles</key>
|
||||
<array>
|
||||
<string>BlueIcon@3x.png</string>
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
"NSContactsUsageDescription" = "Telegram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices.";
|
||||
"NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Telegram needs access to show them a map.";
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you choose to share your Live Location with friends in a chat, Telegram needs background access to your location to keep them updated for the duration of the live sharing.";
|
||||
"NSLocationAlwaysUsageDescription" = "When you choose to share your live location with friends in a chat, Telegram needs background access to your location to keep them updated for the duration of the live sharing. You also need this to send locations from an Apple Watch.";
|
||||
/* GHOSTGRAM: App display name */
|
||||
"CFBundleDisplayName" = "Ghostgram";
|
||||
"CFBundleName" = "Ghostgram";
|
||||
|
||||
"NSContactsUsageDescription" = "Ghostgram will continuously upload your contacts to its heavily encrypted cloud servers to let you connect with your friends across all your devices.";
|
||||
"NSLocationWhenInUseUsageDescription" = "When you send your location to your friends, Ghostgram needs access to show them a map.";
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "When you choose to share your Live Location with friends in a chat, Ghostgram needs background access to your location to keep them updated for the duration of the live sharing.";
|
||||
"NSLocationAlwaysUsageDescription" = "When you choose to share your live location with friends in a chat, Ghostgram needs background access to your location to keep them updated for the duration of the live sharing. You also need this to send locations from an Apple Watch.";
|
||||
"NSCameraUsageDescription" = "We need this so that you can take and share photos and videos, as well as make video calls.";
|
||||
"NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library.";
|
||||
"NSPhotoLibraryAddUsageDescription" = "We need this so that you can save photos and videos to your photo library.";
|
||||
"NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library and save the ones you capture.";
|
||||
"NSPhotoLibraryAddUsageDescription" = "We need this so that you can save photos and videos to your photo library and save the ones you capture.";
|
||||
"NSMicrophoneUsageDescription" = "We need this so that you can record and share voice messages and videos with sound.";
|
||||
"NSSiriUsageDescription" = "You can use Siri to send messages.";
|
||||
"NSFaceIDUsageDescription" = "You can use Face ID to unlock the app.";
|
||||
|
||||
@@ -4856,12 +4856,9 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Conversation.SendMessage.SetReminder" = "Set a Reminder";
|
||||
|
||||
"Conversation.SelectedMessages_1" = "%@ Selected";
|
||||
"Conversation.SelectedMessages_2" = "%@ Selected";
|
||||
"Conversation.SelectedMessages_3_10" = "%@ Selected";
|
||||
"Conversation.SelectedMessages_any" = "%@ Selected";
|
||||
"Conversation.SelectedMessages_many" = "%@ Selected";
|
||||
"Conversation.SelectedMessages_0" = "%@ Selected";
|
||||
"Conversation.SelectedMessagesFormat_1" = "{} Selected";
|
||||
"Conversation.SelectedMessagesFormat_any" = "{} Selected";
|
||||
|
||||
|
||||
"AccentColor.Title" = "Accent Color";
|
||||
|
||||
@@ -15459,6 +15456,8 @@ Error: %8$@";
|
||||
"Notification.StarsGiftOffer.Expired" = "%1$@ didn't respond to your offer for %2$@ within %3$@ – your %4$@ have been refunded";
|
||||
"Notification.StarsGiftOffer.Expired.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.Expired.Stars_any" = "%@ Stars";
|
||||
"Notification.StarsGiftOffer.Expired.Hours_1" = "%@ hours";
|
||||
"Notification.StarsGiftOffer.Expired.Hours_any" = "%@ hours";
|
||||
"Notification.StarsGiftOffer.ExpiredYou" = "The offer from %1$@ to buy your %2$@ for %3$@ has expired";
|
||||
"Notification.StarsGiftOffer.ExpiredYou.Stars_1" = "%@ Star";
|
||||
"Notification.StarsGiftOffer.ExpiredYou.Stars_any" = "%@ Stars";
|
||||
@@ -15623,3 +15622,50 @@ Error: %8$@";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.BadValue" = "The value of this gift is **%@** higher than the offer.";
|
||||
"Chat.GiftPurchaseOffer.AcceptConfirmation.Confirm" = "Confirm Sale";
|
||||
|
||||
"Notification.PremiumGift.DaysTitle_1" = "%@ Day Premium";
|
||||
"Notification.PremiumGift.DaysTitle_any" = "%@ Days Premium";
|
||||
|
||||
"MediaEditor.Audio.Title" = "Audio";
|
||||
"MediaEditor.Audio.SavedMusic" = "SAVED MUSIC";
|
||||
"MediaEditor.Audio.ShowMore" = "Show More";
|
||||
|
||||
"WebApp.AddToAttachmentTitle" = "Add to Attachment Menu";
|
||||
|
||||
"Gift.Upgrade.Wearable.Title" = "Wearable";
|
||||
"Gift.Upgrade.Wearable.Text" = "Display gifts on your page and set them as profile covers or statuses.";
|
||||
"Gift.Upgrade.ViewAllVariants" = "View all variants";
|
||||
|
||||
"CocoonInfo.Title" = "COCOON";
|
||||
"CocoonInfo.Description" = "**Cocoon** (**Co**nfidential **Co**mpute **O**pen **N**etwork) handles AI tasks **safely** and **efficiently**.";
|
||||
"CocoonInfo.Private.Title" = "Private";
|
||||
"CocoonInfo.Private.Text" = "No third party can access any data, such as translations, inside Cocoon.";
|
||||
"CocoonInfo.Efficient.Title" = "Efficient";
|
||||
"CocoonInfo.Efficient.Text" = "Cocoon has allowed Telegram to reduce translation costs by 6x.";
|
||||
"CocoonInfo.ForEveryone.Title" = "For Everyone";
|
||||
"CocoonInfo.ForEveryone.Text" = "Any developer can use Cocoon for AI features. Learn more at [@cocoon](telegram) or [cocoon.org](web).";
|
||||
"CocoonInfo.IntergrateInfo" = "Want to integrate Cocoon into your projects?\nReach out at [t.me/cocoon?direct]()";
|
||||
"CocoonInfo.Understood" = "Understood";
|
||||
|
||||
"Conversation.Translation.CocoonInfo" = "Translations are powered by\n**#Cocoon**. [How does it work?]()";
|
||||
|
||||
"Conversation.EmojiStake.Won" = "%1$@ won %2$@";
|
||||
"Conversation.EmojiStake.WonYou" = "You won %@";
|
||||
"Conversation.EmojiStake.Lost" = "%1$@ lost %2$@";
|
||||
"Conversation.EmojiStake.LostYou" = "You lost %@";
|
||||
|
||||
"Conversation.Dice.Stake" = "Stake:";
|
||||
"Conversation.Dice.Change" = "change";
|
||||
|
||||
"EmojiStake.Title" = "Emoji Stake";
|
||||
"EmojiStake.Description" = "An experimental feature for Telegram Premium users.";
|
||||
"EmojiStake.ResultsTitle" = "RESULTS AND RETURNS";
|
||||
"EmojiStake.StreakInfo" = "A streak resets after 3 # or a stake change.";
|
||||
"EmojiStake.StakeTitle" = "STAKE";
|
||||
"EmojiStake.StakePlaceholder" = "Amount";
|
||||
"EmojiStake.Roll" = "Save and Roll";
|
||||
|
||||
"Conversation.Summary.Title" = "AI Summary";
|
||||
"Conversation.Summary.Text" = "Tap to see original text";
|
||||
|
||||
"Conversation.Summary.Limit.Title" = "AI Summary";
|
||||
"Conversation.Summary.Limit.Text" = "Summarize large messages with AI – unlimited with Telegram Premium.";
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
"NSContactsUsageDescription" = "Актуальная информация о ваших контактах будет храниться зашифрованной в облаке Telegram, чтобы вы могли связаться с друзьями с любого устройства.";
|
||||
"NSLocationWhenInUseUsageDescription" = "Когда вы отправляете друзьям геопозицию, Telegram нужно разрешение, чтобы показать им карту.";
|
||||
/* GHOSTGRAM: App display name */
|
||||
"CFBundleDisplayName" = "Ghostgram";
|
||||
"CFBundleName" = "Ghostgram";
|
||||
|
||||
"NSContactsUsageDescription" = "Актуальная информация о ваших контактах будет храниться зашифрованной в облаке Ghostgram, чтобы вы могли связаться с друзьями с любого устройства.";
|
||||
"NSLocationWhenInUseUsageDescription" = "Когда вы отправляете друзьям геопозицию, Ghostgram нужно разрешение, чтобы показать им карту.";
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями.";
|
||||
"NSLocationAlwaysUsageDescription" = "Фоновый доступ к геопозиции требуется, чтобы обновлять вашу геопозицию, когда вы транслируете её в чат с друзьями. Он также необходим для отправки геопозиции с Apple Watch.";
|
||||
"NSCameraUsageDescription" = "Это необходимо, чтобы вы могли делиться снятыми фотографиями и видео.";
|
||||
@@ -10,3 +14,4 @@
|
||||
"NSMicrophoneUsageDescription" = "Это необходимо, чтобы вы могли делиться голосовыми сообщениями и видео со звуком.";
|
||||
"NSSiriUsageDescription" = "Вы можете использовать Siri для отправки сообщений.";
|
||||
"NSFaceIDUsageDescription" = "Вы можете разблокировать приложение с помощью Face ID.";
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/private/var/tmp/_bazel_ichmagmaus812/b1f80ab1863cefaee5ee828c8e6100cf/execroot/_main
|
||||
@@ -5,40 +5,165 @@ import shutil
|
||||
import tempfile
|
||||
import plistlib
|
||||
import argparse
|
||||
import subprocess
|
||||
import base64
|
||||
|
||||
from BuildEnvironment import run_executable_with_output, check_run_system
|
||||
|
||||
|
||||
def get_certificate_base64():
|
||||
certificate_data = run_executable_with_output('security', arguments=['find-certificate', '-c', 'Apple Distribution: Telegram FZ-LLC (C67CF9S4VU)', '-p'])
|
||||
certificate_data = certificate_data.replace('-----BEGIN CERTIFICATE-----', '')
|
||||
certificate_data = certificate_data.replace('-----END CERTIFICATE-----', '')
|
||||
certificate_data = certificate_data.replace('\n', '')
|
||||
return certificate_data
|
||||
def setup_temp_keychain(p12_path, p12_password=''):
|
||||
"""Create a temporary keychain and import the p12 certificate."""
|
||||
keychain_name = 'generate-profiles-temp.keychain'
|
||||
keychain_password = 'temp123'
|
||||
|
||||
# Delete if exists
|
||||
run_executable_with_output('security', arguments=['delete-keychain', keychain_name], check_result=False)
|
||||
|
||||
# Create keychain
|
||||
run_executable_with_output('security', arguments=[
|
||||
'create-keychain', '-p', keychain_password, keychain_name
|
||||
], check_result=True)
|
||||
|
||||
# Add to search list
|
||||
existing = run_executable_with_output('security', arguments=['list-keychains', '-d', 'user'])
|
||||
run_executable_with_output('security', arguments=[
|
||||
'list-keychains', '-d', 'user', '-s', keychain_name, existing.replace('"', '')
|
||||
], check_result=True)
|
||||
|
||||
# Unlock and set settings
|
||||
run_executable_with_output('security', arguments=['set-keychain-settings', keychain_name])
|
||||
run_executable_with_output('security', arguments=[
|
||||
'unlock-keychain', '-p', keychain_password, keychain_name
|
||||
])
|
||||
|
||||
# Import p12
|
||||
run_executable_with_output('security', arguments=[
|
||||
'import', p12_path, '-k', keychain_name, '-P', p12_password,
|
||||
'-T', '/usr/bin/codesign', '-T', '/usr/bin/security'
|
||||
], check_result=True)
|
||||
|
||||
# Set partition list for access
|
||||
run_executable_with_output('security', arguments=[
|
||||
'set-key-partition-list', '-S', 'apple-tool:,apple:', '-k', keychain_password, keychain_name
|
||||
], check_result=True)
|
||||
|
||||
return keychain_name
|
||||
|
||||
|
||||
def process_provisioning_profile(source, destination, certificate_data):
|
||||
def cleanup_temp_keychain(keychain_name):
|
||||
"""Remove the temporary keychain."""
|
||||
run_executable_with_output('security', arguments=['delete-keychain', keychain_name], check_result=False)
|
||||
|
||||
|
||||
def get_signing_identity_from_p12(p12_path, p12_password=''):
|
||||
"""Extract the common name (signing identity) from the p12 certificate."""
|
||||
proc = subprocess.Popen(
|
||||
['openssl', 'pkcs12', '-in', p12_path, '-passin', 'pass:' + p12_password, '-nokeys', '-legacy'],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
cert_pem, _ = proc.communicate()
|
||||
|
||||
proc2 = subprocess.Popen(
|
||||
['openssl', 'x509', '-noout', '-subject', '-nameopt', 'oneline,-esc_msb'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
subject, _ = proc2.communicate(cert_pem)
|
||||
subject = subject.decode('utf-8').strip()
|
||||
|
||||
# Parse CN from subject line like: subject= C = AE, O = ..., CN = Some Name
|
||||
if 'CN = ' in subject:
|
||||
cn = subject.split('CN = ')[-1].split(',')[0].strip()
|
||||
return cn
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_certificate_base64_from_p12(p12_path, p12_password=''):
|
||||
"""Extract the certificate as base64 from p12 file."""
|
||||
# Extract certificate in PEM format
|
||||
proc = subprocess.Popen(
|
||||
['openssl', 'pkcs12', '-in', p12_path, '-passin', 'pass:' + p12_password, '-nokeys', '-legacy'],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
cert_pem, _ = proc.communicate()
|
||||
|
||||
# Convert to DER format
|
||||
proc2 = subprocess.Popen(
|
||||
['openssl', 'x509', '-outform', 'DER'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||
)
|
||||
cert_der, _ = proc2.communicate(cert_pem)
|
||||
|
||||
return base64.b64encode(cert_der).decode('utf-8')
|
||||
|
||||
|
||||
def process_provisioning_profile(source, destination, certificate_data, signing_identity, keychain_name):
|
||||
parsed_plist = run_executable_with_output('security', arguments=['cms', '-D', '-i', source], check_result=True)
|
||||
parsed_plist_file = tempfile.mktemp()
|
||||
with open(parsed_plist_file, 'w+') as file:
|
||||
file.write(parsed_plist)
|
||||
|
||||
run_executable_with_output('plutil', arguments=['-remove', 'DeveloperCertificates.0', parsed_plist_file])
|
||||
# Remove all existing developer certificates
|
||||
while True:
|
||||
result = run_executable_with_output('plutil', arguments=['-remove', 'DeveloperCertificates.0', parsed_plist_file], check_result=False)
|
||||
if result is None or 'Could not' in str(result) or result == '':
|
||||
# Check if the removal actually failed by trying to extract
|
||||
check = run_executable_with_output('plutil', arguments=['-extract', 'DeveloperCertificates.0', 'raw', parsed_plist_file], check_result=False)
|
||||
if check is None or 'Could not' in str(check):
|
||||
break
|
||||
|
||||
# Insert the new certificate
|
||||
run_executable_with_output('plutil', arguments=['-insert', 'DeveloperCertificates.0', '-data', certificate_data, parsed_plist_file])
|
||||
|
||||
# Remove the DER-Encoded-Profile (signature)
|
||||
run_executable_with_output('plutil', arguments=['-remove', 'DER-Encoded-Profile', parsed_plist_file])
|
||||
|
||||
run_executable_with_output('security', arguments=['cms', '-S', '-N', 'Apple Distribution: Telegram FZ-LLC (C67CF9S4VU)', '-i', parsed_plist_file, '-o', destination])
|
||||
# Sign with the certificate from the temporary keychain
|
||||
run_executable_with_output('security', arguments=[
|
||||
'cms', '-S', '-k', keychain_name, '-N', signing_identity, '-i', parsed_plist_file, '-o', destination
|
||||
], check_result=True)
|
||||
|
||||
os.unlink(parsed_plist_file)
|
||||
|
||||
|
||||
def generate_provisioning_profiles(source_path, destination_path):
|
||||
certificate_data = get_certificate_base64()
|
||||
def generate_provisioning_profiles(source_path, destination_path, certs_path):
|
||||
p12_path = os.path.join(certs_path, 'SelfSigned.p12')
|
||||
|
||||
if not os.path.exists(destination_path):
|
||||
print('{} does not exits'.format(destination_path))
|
||||
if not os.path.exists(p12_path):
|
||||
print('{} does not exist'.format(p12_path))
|
||||
sys.exit(1)
|
||||
|
||||
for file_name in os.listdir(source_path):
|
||||
if file_name.endswith('.mobileprovision'):
|
||||
process_provisioning_profile(source=source_path + '/' + file_name, destination=destination_path + '/' + file_name, certificate_data=certificate_data)
|
||||
if not os.path.exists(destination_path):
|
||||
print('{} does not exist'.format(destination_path))
|
||||
sys.exit(1)
|
||||
|
||||
# Extract certificate info from p12
|
||||
p12_password = '' # fake-codesigning uses empty password
|
||||
certificate_data = get_certificate_base64_from_p12(p12_path, p12_password)
|
||||
signing_identity = get_signing_identity_from_p12(p12_path, p12_password)
|
||||
|
||||
if not signing_identity:
|
||||
print('Could not extract signing identity from {}'.format(p12_path))
|
||||
sys.exit(1)
|
||||
|
||||
print('Using signing identity: {}'.format(signing_identity))
|
||||
|
||||
# Setup temporary keychain with the certificate
|
||||
keychain_name = setup_temp_keychain(p12_path, p12_password)
|
||||
|
||||
try:
|
||||
for file_name in os.listdir(source_path):
|
||||
if file_name.endswith('.mobileprovision'):
|
||||
print('Processing {}'.format(file_name))
|
||||
process_provisioning_profile(
|
||||
source=os.path.join(source_path, file_name),
|
||||
destination=os.path.join(destination_path, file_name),
|
||||
certificate_data=certificate_data,
|
||||
signing_identity=signing_identity,
|
||||
keychain_name=keychain_name
|
||||
)
|
||||
print('Done. Generated {} profiles.'.format(
|
||||
len([f for f in os.listdir(destination_path) if f.endswith('.mobileprovision')])
|
||||
))
|
||||
finally:
|
||||
cleanup_temp_keychain(keychain_name)
|
||||
|
||||
@@ -46,6 +46,7 @@ class BazelCommandLine:
|
||||
self.show_actions = False
|
||||
self.enable_sandbox = False
|
||||
self.disable_provisioning_profiles = False
|
||||
self.profile_swift = False
|
||||
|
||||
self.common_args = [
|
||||
# https://docs.bazel.build/versions/master/command-line-reference.html
|
||||
@@ -143,6 +144,9 @@ class BazelCommandLine:
|
||||
def set_disable_provisioning_profiles(self):
|
||||
self.disable_provisioning_profiles = True
|
||||
|
||||
def set_profile_swift(self, value):
|
||||
self.profile_swift = value
|
||||
|
||||
def set_configuration(self, configuration):
|
||||
if configuration == 'debug_arm64':
|
||||
self.configuration_args = [
|
||||
@@ -300,6 +304,8 @@ class BazelCommandLine:
|
||||
]
|
||||
|
||||
combined_arguments += self.configuration_args
|
||||
if self.profile_swift:
|
||||
combined_arguments += ['--config=swift_profile']
|
||||
|
||||
print('TelegramBuild: running')
|
||||
print(subprocess.list2cmdline(combined_arguments))
|
||||
@@ -369,17 +375,15 @@ class BazelCommandLine:
|
||||
print(subprocess.list2cmdline(combined_arguments))
|
||||
call_executable(combined_arguments)
|
||||
|
||||
def get_spm_aspect_invocation(self):
|
||||
def invoke_spm_build(self):
|
||||
combined_arguments = [
|
||||
self.build_environment.bazel_path
|
||||
]
|
||||
combined_arguments += self.get_startup_bazel_arguments()
|
||||
combined_arguments += ['build']
|
||||
|
||||
if self.custom_target is not None:
|
||||
combined_arguments += [self.custom_target]
|
||||
else:
|
||||
combined_arguments += ['Telegram/Telegram']
|
||||
# Build the generate_spm target directly to get the dependency tree JSON
|
||||
combined_arguments += ['//Telegram:spm_build_root']
|
||||
|
||||
if self.continue_on_error:
|
||||
combined_arguments += ['--keep_going']
|
||||
@@ -409,8 +413,6 @@ class BazelCommandLine:
|
||||
|
||||
combined_arguments += self.configuration_args
|
||||
|
||||
combined_arguments += ['--aspects', '//build-system/bazel-utils:spm.bzl%spm_text_aspect']
|
||||
|
||||
print(subprocess.list2cmdline(combined_arguments))
|
||||
call_executable(combined_arguments)
|
||||
|
||||
@@ -624,6 +626,7 @@ def build(bazel, arguments):
|
||||
bazel_command_line.set_continue_on_error(arguments.continueOnError)
|
||||
bazel_command_line.set_show_actions(arguments.showActions)
|
||||
bazel_command_line.set_enable_sandbox(arguments.sandbox)
|
||||
bazel_command_line.set_profile_swift(arguments.profileSwift)
|
||||
|
||||
bazel_command_line.set_split_swiftmodules(arguments.enableParallelSwiftmoduleGeneration)
|
||||
|
||||
@@ -719,7 +722,7 @@ def query(bazel, arguments):
|
||||
bazel_command_line.invoke_query(query_args)
|
||||
|
||||
|
||||
def get_spm_aspect_invocation(bazel, arguments):
|
||||
def build_spm(bazel, arguments):
|
||||
bazel_command_line = BazelCommandLine(
|
||||
bazel=bazel,
|
||||
override_bazel_version=arguments.overrideBazelVersion,
|
||||
@@ -741,13 +744,12 @@ def get_spm_aspect_invocation(bazel, arguments):
|
||||
|
||||
bazel_command_line.set_configuration(arguments.configuration)
|
||||
bazel_command_line.set_build_number(arguments.buildNumber)
|
||||
bazel_command_line.set_custom_target(arguments.target)
|
||||
bazel_command_line.set_continue_on_error(False)
|
||||
bazel_command_line.set_show_actions(False)
|
||||
bazel_command_line.set_enable_sandbox(False)
|
||||
bazel_command_line.set_split_swiftmodules(False)
|
||||
|
||||
bazel_command_line.get_spm_aspect_invocation()
|
||||
bazel_command_line.invoke_spm_build()
|
||||
|
||||
def add_codesigning_common_arguments(current_parser: argparse.ArgumentParser):
|
||||
configuration_group = current_parser.add_mutually_exclusive_group(required=True)
|
||||
@@ -977,6 +979,12 @@ if __name__ == '__main__':
|
||||
help='Generate .swiftmodule files in parallel to building modules, can speed up compilation on multi-core '
|
||||
'systems. '
|
||||
)
|
||||
buildParser.add_argument(
|
||||
'--profileSwift',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Enable single-core Swift compile profiling flags.'
|
||||
)
|
||||
buildParser.add_argument(
|
||||
'--target',
|
||||
type=str,
|
||||
@@ -1068,6 +1076,13 @@ if __name__ == '__main__':
|
||||
type=str,
|
||||
help='Path to the destination directory.'
|
||||
)
|
||||
generate_profiles_build_parser.add_argument(
|
||||
'--certsPath',
|
||||
required=False,
|
||||
type=str,
|
||||
default='build-system/fake-codesigning/certs',
|
||||
help='Path to the directory containing SelfSigned.p12 certificate.'
|
||||
)
|
||||
|
||||
remote_upload_testflight_parser = subparsers.add_parser('remote-deploy-testflight', help='Build the app using a remote environment.')
|
||||
remote_upload_testflight_parser.add_argument(
|
||||
@@ -1188,13 +1203,7 @@ if __name__ == '__main__':
|
||||
metavar='query_string'
|
||||
)
|
||||
|
||||
spm_parser = subparsers.add_parser('spm', help='Generate SPM package')
|
||||
spm_parser.add_argument(
|
||||
'--target',
|
||||
type=str,
|
||||
help='A custom bazel target name to build.',
|
||||
metavar='target_name'
|
||||
)
|
||||
spm_parser = subparsers.add_parser('spm', help='Generate SPM package (outputs bazel-bin/Telegram/spm_build_root_modules.json)')
|
||||
spm_parser.add_argument(
|
||||
'--buildNumber',
|
||||
required=False,
|
||||
@@ -1315,7 +1324,7 @@ if __name__ == '__main__':
|
||||
additional_codesigning_output_path=remote_input_path
|
||||
)
|
||||
|
||||
GenerateProfiles.generate_provisioning_profiles(source_path=remote_input_path + '/profiles', destination_path=args.destination)
|
||||
GenerateProfiles.generate_provisioning_profiles(source_path=remote_input_path + '/profiles', destination_path=args.destination, certs_path=args.certsPath)
|
||||
elif args.commandName == 'remote-deploy-testflight':
|
||||
env = os.environ
|
||||
if 'APPSTORE_CONNECT_USERNAME' not in env:
|
||||
@@ -1351,7 +1360,7 @@ if __name__ == '__main__':
|
||||
elif args.commandName == 'query':
|
||||
query(bazel=bazel_path, arguments=args)
|
||||
elif args.commandName == 'spm':
|
||||
get_spm_aspect_invocation(bazel=bazel_path, arguments=args)
|
||||
build_spm(bazel=bazel_path, arguments=args)
|
||||
else:
|
||||
raise Exception('Unknown command')
|
||||
except KeyboardInterrupt:
|
||||
|
||||
@@ -604,7 +604,7 @@ def remote_build_tart(macos_version, bazel_cache_host, configuration, build_inpu
|
||||
else:
|
||||
guest_build_sh += '--cacheHost="$CACHE_HOST" \\'
|
||||
guest_build_sh += 'build \\'
|
||||
guest_build_sh += '--lock \\'
|
||||
#guest_build_sh += '--lock \\'
|
||||
guest_build_sh += '--buildNumber={} \\'.format(build_number)
|
||||
guest_build_sh += '--configuration={} \\'.format(configuration)
|
||||
guest_build_sh += '--configurationPath=$HOME/telegram-build-input/configuration.json \\'
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "aexml",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tadija/AEXML.git",
|
||||
"state" : {
|
||||
"revision" : "38f7d00b23ecd891e1ee656fa6aeebd6ba04ecc3",
|
||||
"version" : "4.6.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "pathkit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kylef/PathKit.git",
|
||||
"state" : {
|
||||
"revision" : "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
|
||||
"version" : "1.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "spectre",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/kylef/Spectre.git",
|
||||
"state" : {
|
||||
"revision" : "26cc5e9ae0947092c7139ef7ba612e34646086c7",
|
||||
"version" : "0.10.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-argument-parser",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/apple/swift-argument-parser",
|
||||
"state" : {
|
||||
"revision" : "309a47b2b1d9b5e991f36961c983ecec72275be3",
|
||||
"version" : "1.6.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "xcodeproj",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/tuist/XcodeProj.git",
|
||||
"state" : {
|
||||
"revision" : "dc3b87a4e69f9cd06c6cb16199f5d0472e57ef6b",
|
||||
"version" : "8.24.3"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// swift-tools-version:5.8
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MakeProject",
|
||||
platforms: [
|
||||
.macOS(.v13)
|
||||
],
|
||||
products: [
|
||||
.executable(name: "MakeProject", targets: ["MakeProject"])
|
||||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/tuist/XcodeProj.git", from: "8.15.0"),
|
||||
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "MakeProject",
|
||||
dependencies: [
|
||||
.product(name: "XcodeProj", package: "XcodeProj"),
|
||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
import Foundation
|
||||
|
||||
struct ModuleDefinition: Codable {
|
||||
let name: String
|
||||
let moduleName: String?
|
||||
let type: String
|
||||
let path: String
|
||||
let sources: [String]
|
||||
let deps: [String]?
|
||||
let copts: [String]?
|
||||
let cxxopts: [String]?
|
||||
let defines: [String]?
|
||||
let includes: [String]?
|
||||
let sdkFrameworks: [String]?
|
||||
let sdkDylibs: [String]?
|
||||
let hdrs: [String]?
|
||||
let textualHdrs: [String]?
|
||||
let weakSdkFrameworks: [String]?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name
|
||||
case moduleName = "module_name"
|
||||
case type
|
||||
case path
|
||||
case sources
|
||||
case deps
|
||||
case copts
|
||||
case cxxopts
|
||||
case defines
|
||||
case includes
|
||||
case sdkFrameworks = "sdk_frameworks"
|
||||
case sdkDylibs = "sdk_dylibs"
|
||||
case hdrs
|
||||
case textualHdrs = "textual_hdrs"
|
||||
case weakSdkFrameworks = "weak_sdk_frameworks"
|
||||
}
|
||||
}
|
||||
|
||||
enum ModuleType: String {
|
||||
case swiftLibrary = "swift_library"
|
||||
case objcLibrary = "objc_library"
|
||||
case ccLibrary = "cc_library"
|
||||
case xcframework = "apple_static_xcframework_import"
|
||||
|
||||
init?(from definition: ModuleDefinition) {
|
||||
self.init(rawValue: definition.type)
|
||||
}
|
||||
}
|
||||
|
||||
func loadModules(from path: String) throws -> [String: ModuleDefinition] {
|
||||
let url = URL(fileURLWithPath: path)
|
||||
let data = try Data(contentsOf: url)
|
||||
return try JSONDecoder().decode([String: ModuleDefinition].self, from: data)
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import Foundation
|
||||
import PathKit
|
||||
import XcodeProj
|
||||
|
||||
class ProjectGenerator {
|
||||
let modulesPath: Path
|
||||
let outputDir: Path
|
||||
let projectRoot: Path
|
||||
|
||||
init(modulesPath: Path, outputDir: Path, projectRoot: Path) {
|
||||
self.modulesPath = modulesPath
|
||||
self.outputDir = outputDir
|
||||
self.projectRoot = projectRoot
|
||||
}
|
||||
|
||||
func generate() throws {
|
||||
print("Loading modules from \(modulesPath)...")
|
||||
let modules = try loadModules(from: modulesPath.string)
|
||||
print("Loaded \(modules.count) modules")
|
||||
|
||||
// Filter out empty modules, but keep:
|
||||
// - Modules with source files (excluding .a)
|
||||
// - Static library modules (only .a files)
|
||||
// - XCFramework imports
|
||||
let validModules = modules.filter { name, module in
|
||||
let nonStaticSources = module.sources.filter { !$0.hasSuffix(".a") }
|
||||
let staticLibs = module.sources.filter { $0.hasSuffix(".a") }
|
||||
return !nonStaticSources.isEmpty ||
|
||||
!staticLibs.isEmpty ||
|
||||
module.type == "apple_static_xcframework_import"
|
||||
}
|
||||
print("Processing \(validModules.count) non-empty modules")
|
||||
|
||||
// Setup output directory
|
||||
try outputDir.mkpath()
|
||||
|
||||
// Create symlink manager
|
||||
let symlinkManager = SymlinkManager(outputDir: outputDir, projectRoot: projectRoot)
|
||||
symlinkManager.scanExistingFiles()
|
||||
|
||||
// Create project
|
||||
let projectPath = outputDir + "Telegram.xcodeproj"
|
||||
let pbxproj = PBXProj()
|
||||
|
||||
// Create main group
|
||||
let mainGroup = PBXGroup(children: [], sourceTree: .group)
|
||||
pbxproj.add(object: mainGroup)
|
||||
|
||||
// Create project-level build configurations
|
||||
let projectDebugSettings: BuildSettings = [
|
||||
"ALWAYS_SEARCH_USER_PATHS": "NO",
|
||||
"CLANG_CXX_LANGUAGE_STANDARD": "c++17",
|
||||
"CLANG_ENABLE_MODULES": "YES",
|
||||
"CLANG_ENABLE_OBJC_ARC": "YES",
|
||||
"CLANG_ENABLE_EXPLICIT_MODULES": "NO", // Disable explicit module builds for ObjC-Swift interop
|
||||
"SWIFT_ENABLE_EXPLICIT_MODULES": "NO", // Disable explicit module builds for Swift
|
||||
"ENABLE_STRICT_OBJC_MSGSEND": "YES",
|
||||
"GCC_NO_COMMON_BLOCKS": "YES",
|
||||
"IPHONEOS_DEPLOYMENT_TARGET": "13.0",
|
||||
"MTL_ENABLE_DEBUG_INFO": "INCLUDE_SOURCE",
|
||||
"ONLY_ACTIVE_ARCH": "YES",
|
||||
"SDKROOT": "iphoneos",
|
||||
"SWIFT_VERSION": "5.0",
|
||||
"TARGETED_DEVICE_FAMILY": "1,2",
|
||||
"DEBUG_INFORMATION_FORMAT": "dwarf",
|
||||
"ENABLE_BITCODE": "NO",
|
||||
]
|
||||
|
||||
var projectReleaseSettings = projectDebugSettings
|
||||
projectReleaseSettings["DEBUG_INFORMATION_FORMAT"] = "dwarf-with-dsym"
|
||||
projectReleaseSettings["MTL_ENABLE_DEBUG_INFO"] = "NO"
|
||||
projectReleaseSettings["ONLY_ACTIVE_ARCH"] = "NO"
|
||||
|
||||
let projectDebugConfig = XCBuildConfiguration(name: "Debug", buildSettings: projectDebugSettings)
|
||||
let projectReleaseConfig = XCBuildConfiguration(name: "Release", buildSettings: projectReleaseSettings)
|
||||
pbxproj.add(object: projectDebugConfig)
|
||||
pbxproj.add(object: projectReleaseConfig)
|
||||
|
||||
let projectConfigList = XCConfigurationList(
|
||||
buildConfigurations: [projectDebugConfig, projectReleaseConfig],
|
||||
defaultConfigurationName: "Release"
|
||||
)
|
||||
pbxproj.add(object: projectConfigList)
|
||||
|
||||
// Create project
|
||||
let project = PBXProject(
|
||||
name: "Telegram",
|
||||
buildConfigurationList: projectConfigList,
|
||||
compatibilityVersion: "Xcode 14.0",
|
||||
preferredProjectObjectVersion: 56,
|
||||
minimizedProjectReferenceProxies: 0,
|
||||
mainGroup: mainGroup
|
||||
)
|
||||
pbxproj.add(object: project)
|
||||
|
||||
// Create target builder
|
||||
let targetBuilder = TargetBuilder(
|
||||
project: project,
|
||||
pbxproj: pbxproj,
|
||||
mainGroup: mainGroup,
|
||||
outputDir: outputDir,
|
||||
symlinkManager: symlinkManager
|
||||
)
|
||||
|
||||
// Build targets
|
||||
print("Creating targets...")
|
||||
var builtCount = 0
|
||||
for (name, module) in validModules.sorted(by: { $0.key < $1.key }) {
|
||||
do {
|
||||
if let _ = try targetBuilder.buildTarget(for: module, allModules: validModules) {
|
||||
builtCount += 1
|
||||
if builtCount % 50 == 0 {
|
||||
print(" Created \(builtCount) targets...")
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Warning: Failed to build target \(name): \(error)")
|
||||
}
|
||||
}
|
||||
print("Created \(builtCount) targets")
|
||||
|
||||
// Wire up dependencies
|
||||
print("Wiring up dependencies...")
|
||||
try targetBuilder.wireUpDependencies(modules: validModules)
|
||||
|
||||
// Write project
|
||||
print("Writing project to \(projectPath)...")
|
||||
pbxproj.rootObject = project
|
||||
let xcodeproj = XcodeProj(workspace: XCWorkspace(), pbxproj: pbxproj)
|
||||
try xcodeproj.write(path: projectPath)
|
||||
|
||||
// Generate scheme for main target
|
||||
if let telegramTarget = targetBuilder.getTarget(named: "TelegramUI") {
|
||||
print("Generating scheme...")
|
||||
let schemeGenerator = SchemeGenerator(projectPath: projectPath, pbxproj: pbxproj)
|
||||
try schemeGenerator.generateScheme(for: telegramTarget, named: "TelegramUI")
|
||||
} else {
|
||||
print("Warning: Could not find TelegramUI target for scheme")
|
||||
}
|
||||
|
||||
// Clean up stale files
|
||||
print("Cleaning up stale symlinks...")
|
||||
symlinkManager.cleanupStaleFiles()
|
||||
|
||||
print("Done! Project written to \(projectPath)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import Foundation
|
||||
import PathKit
|
||||
import XcodeProj
|
||||
|
||||
class SchemeGenerator {
|
||||
let projectPath: Path
|
||||
let pbxproj: PBXProj
|
||||
|
||||
init(projectPath: Path, pbxproj: PBXProj) {
|
||||
self.projectPath = projectPath
|
||||
self.pbxproj = pbxproj
|
||||
}
|
||||
|
||||
func generateScheme(for target: PBXNativeTarget, named schemeName: String) throws {
|
||||
let schemesDir = projectPath + "xcshareddata" + "xcschemes"
|
||||
try schemesDir.mkpath()
|
||||
|
||||
let buildableReference = XCScheme.BuildableReference(
|
||||
referencedContainer: "container:Telegram.xcodeproj",
|
||||
blueprint: target,
|
||||
buildableName: "\(target.name).framework",
|
||||
blueprintName: target.name
|
||||
)
|
||||
|
||||
let buildAction = XCScheme.BuildAction(
|
||||
buildActionEntries: [
|
||||
XCScheme.BuildAction.Entry(
|
||||
buildableReference: buildableReference,
|
||||
buildFor: [.running, .testing, .profiling, .archiving, .analyzing]
|
||||
)
|
||||
],
|
||||
parallelizeBuild: true,
|
||||
buildImplicitDependencies: true
|
||||
)
|
||||
|
||||
let launchAction = XCScheme.LaunchAction(
|
||||
runnable: nil,
|
||||
buildConfiguration: "Debug"
|
||||
)
|
||||
|
||||
let testAction = XCScheme.TestAction(
|
||||
buildConfiguration: "Debug",
|
||||
macroExpansion: buildableReference
|
||||
)
|
||||
|
||||
let profileAction = XCScheme.ProfileAction(
|
||||
runnable: nil,
|
||||
buildConfiguration: "Release",
|
||||
macroExpansion: buildableReference
|
||||
)
|
||||
|
||||
let analyzeAction = XCScheme.AnalyzeAction(buildConfiguration: "Debug")
|
||||
|
||||
let archiveAction = XCScheme.ArchiveAction(
|
||||
buildConfiguration: "Release",
|
||||
revealArchiveInOrganizer: true
|
||||
)
|
||||
|
||||
let scheme = XCScheme(
|
||||
name: schemeName,
|
||||
lastUpgradeVersion: nil,
|
||||
version: nil,
|
||||
buildAction: buildAction,
|
||||
testAction: testAction,
|
||||
launchAction: launchAction,
|
||||
profileAction: profileAction,
|
||||
analyzeAction: analyzeAction,
|
||||
archiveAction: archiveAction
|
||||
)
|
||||
|
||||
let schemePath = schemesDir + "\(schemeName).xcscheme"
|
||||
try scheme.write(path: schemePath, override: true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import Foundation
|
||||
import PathKit
|
||||
|
||||
class SymlinkManager {
|
||||
let outputDir: Path
|
||||
let projectRoot: Path
|
||||
private var previousFiles: Set<Path> = []
|
||||
private var currentFiles: Set<Path> = []
|
||||
|
||||
init(outputDir: Path, projectRoot: Path) {
|
||||
self.outputDir = outputDir
|
||||
self.projectRoot = projectRoot
|
||||
}
|
||||
|
||||
func scanExistingFiles() {
|
||||
previousFiles = []
|
||||
scanDirectory(outputDir)
|
||||
}
|
||||
|
||||
private func scanDirectory(_ path: Path) {
|
||||
guard path.exists else { return }
|
||||
do {
|
||||
for item in try path.children() {
|
||||
let name = item.lastComponent
|
||||
// Skip build artifacts and xcodeproj bundles
|
||||
if name == ".build" || name.hasSuffix(".xcodeproj") { continue }
|
||||
previousFiles.insert(item)
|
||||
if item.isDirectory && !item.isSymlink {
|
||||
scanDirectory(item)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Warning: Could not scan \(path): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func createDirectory(_ path: Path) throws {
|
||||
currentFiles.insert(path)
|
||||
var parent = path.parent()
|
||||
while parent != outputDir && parent.string.count > outputDir.string.count {
|
||||
currentFiles.insert(parent)
|
||||
parent = parent.parent()
|
||||
}
|
||||
if !path.exists {
|
||||
try path.mkpath()
|
||||
}
|
||||
}
|
||||
|
||||
func createSymlink(from source: Path, to target: Path) throws {
|
||||
currentFiles.insert(target)
|
||||
var parent = target.parent()
|
||||
while parent != outputDir && parent.string.count > outputDir.string.count {
|
||||
currentFiles.insert(parent)
|
||||
parent = parent.parent()
|
||||
}
|
||||
|
||||
// Calculate relative path from target back to source
|
||||
let targetDir = target.parent()
|
||||
let depth = targetDir.components.count - outputDir.components.count + 1
|
||||
let relativePrefix = Array(repeating: "..", count: depth).joined(separator: "/")
|
||||
let relativePath = Path(relativePrefix) + source
|
||||
|
||||
if target.isSymlink {
|
||||
let existingTarget = try? target.symlinkDestination()
|
||||
if existingTarget == relativePath {
|
||||
return // Already correct
|
||||
}
|
||||
try target.delete()
|
||||
} else if target.exists {
|
||||
try target.delete()
|
||||
}
|
||||
|
||||
try targetDir.mkpath()
|
||||
try FileManager.default.createSymbolicLink(
|
||||
atPath: target.string,
|
||||
withDestinationPath: relativePath.string
|
||||
)
|
||||
}
|
||||
|
||||
func cleanupStaleFiles() {
|
||||
let staleFiles = previousFiles.subtracting(currentFiles)
|
||||
let sortedStale = staleFiles.sorted { $0.components.count > $1.components.count }
|
||||
|
||||
for path in sortedStale {
|
||||
do {
|
||||
if path.isSymlink || path.isFile {
|
||||
try path.delete()
|
||||
} else if path.isDirectory {
|
||||
if (try? path.children().isEmpty) == true {
|
||||
try path.delete()
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
print("Warning: Could not remove \(path): \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func markFile(_ path: Path) {
|
||||
currentFiles.insert(path)
|
||||
}
|
||||
}
|
||||
|
||||
extension Path {
|
||||
var isSymlink: Bool {
|
||||
var isDir: ObjCBool = false
|
||||
let exists = FileManager.default.fileExists(atPath: self.string, isDirectory: &isDir)
|
||||
guard exists else { return false }
|
||||
do {
|
||||
let attrs = try FileManager.default.attributesOfItem(atPath: self.string)
|
||||
return attrs[.type] as? FileAttributeType == .typeSymbolicLink
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,792 @@
|
||||
import Foundation
|
||||
import PathKit
|
||||
import XcodeProj
|
||||
|
||||
class TargetBuilder {
|
||||
let project: PBXProject
|
||||
let pbxproj: PBXProj
|
||||
let mainGroup: PBXGroup
|
||||
let outputDir: Path
|
||||
let symlinkManager: SymlinkManager
|
||||
|
||||
private var targetsByName: [String: PBXNativeTarget] = [:]
|
||||
private var groupsByPath: [String: PBXGroup] = [:]
|
||||
|
||||
init(project: PBXProject, pbxproj: PBXProj, mainGroup: PBXGroup, outputDir: Path, symlinkManager: SymlinkManager) {
|
||||
self.project = project
|
||||
self.pbxproj = pbxproj
|
||||
self.mainGroup = mainGroup
|
||||
self.outputDir = outputDir
|
||||
self.symlinkManager = symlinkManager
|
||||
}
|
||||
|
||||
// Track which modules are header-only (have no linkable code)
|
||||
private var headerOnlyModules: Set<String> = []
|
||||
// Track which modules are static library collections (.a files)
|
||||
private var staticLibraryModules: [String: [String]] = [:] // module name -> list of .a file paths
|
||||
|
||||
func isHeaderOnlyModule(_ name: String) -> Bool {
|
||||
return headerOnlyModules.contains(name)
|
||||
}
|
||||
|
||||
func isStaticLibraryModule(_ name: String) -> Bool {
|
||||
return staticLibraryModules[name] != nil
|
||||
}
|
||||
|
||||
func getStaticLibraries(for name: String) -> [String] {
|
||||
return staticLibraryModules[name] ?? []
|
||||
}
|
||||
|
||||
func buildTarget(for module: ModuleDefinition, allModules: [String: ModuleDefinition]) throws -> PBXNativeTarget? {
|
||||
guard let moduleType = ModuleType(from: module) else {
|
||||
print("Warning: Unknown module type \(module.type) for \(module.name)")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for static library modules (only .a files)
|
||||
let staticLibs = module.sources.filter { $0.hasSuffix(".a") }
|
||||
let nonStaticLibs = module.sources.filter { !$0.hasSuffix(".a") }
|
||||
let isStaticLibOnly = !staticLibs.isEmpty && nonStaticLibs.allSatisfy { $0.hasSuffix(".h") || $0.hasSuffix(".hpp") }
|
||||
|
||||
if isStaticLibOnly && moduleType != .xcframework {
|
||||
// This is a static library module - track its .a files but don't create a framework
|
||||
staticLibraryModules[module.name] = staticLibs
|
||||
return nil // Don't create a target, just track the static libs
|
||||
}
|
||||
|
||||
// Check if module is header-only (only header files, no real sources)
|
||||
let sourceFiles = module.sources.filter { source in
|
||||
!source.hasSuffix(".a") && !source.hasSuffix(".h") && !source.hasSuffix(".hpp")
|
||||
}
|
||||
let isHeaderOnly = sourceFiles.isEmpty && moduleType != .xcframework
|
||||
|
||||
// Skip modules with no sources at all
|
||||
if module.sources.isEmpty && moduleType != .xcframework {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch moduleType {
|
||||
case .xcframework:
|
||||
return try buildXCFrameworkTarget(for: module)
|
||||
case .swiftLibrary, .objcLibrary, .ccLibrary:
|
||||
if isHeaderOnly {
|
||||
headerOnlyModules.insert(module.name)
|
||||
return try buildHeaderOnlyFrameworkTarget(for: module)
|
||||
} else {
|
||||
return try buildFrameworkTarget(for: module, moduleType: moduleType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func buildXCFrameworkTarget(for module: ModuleDefinition) throws -> PBXNativeTarget {
|
||||
// For xcframeworks, we create a reference but no build phases
|
||||
let target = PBXNativeTarget(
|
||||
name: module.name,
|
||||
buildConfigurationList: createConfigurationList(for: module, isXCFramework: true),
|
||||
buildPhases: [],
|
||||
productName: module.name,
|
||||
productType: .framework
|
||||
)
|
||||
pbxproj.add(object: target)
|
||||
project.targets.append(target)
|
||||
targetsByName[module.name] = target
|
||||
return target
|
||||
}
|
||||
|
||||
private func buildFrameworkTarget(for module: ModuleDefinition, moduleType: ModuleType) throws -> PBXNativeTarget {
|
||||
// Create group for module
|
||||
let moduleGroup = try getOrCreateGroup(for: module.path)
|
||||
|
||||
// Create symlinks and file references
|
||||
var sourceRefs: [PBXFileReference] = []
|
||||
var publicHeaderRefs: [PBXFileReference] = []
|
||||
var seenPublicHeaderNames: Set<String> = [] // Track header filenames to avoid duplicates in headers build phase
|
||||
|
||||
// Determine public header detection:
|
||||
// 1. If hdrs is provided, use those (explicit public headers)
|
||||
// 2. Otherwise, headers in includes directories are public
|
||||
let explicitPublicHeaders = Set(module.hdrs ?? [])
|
||||
|
||||
let allSourceFiles = module.sources + (module.hdrs ?? []) + (module.textualHdrs ?? [])
|
||||
|
||||
for source in allSourceFiles {
|
||||
if source.hasSuffix(".a") { continue }
|
||||
|
||||
let sourcePath = Path(source)
|
||||
let fileName = sourcePath.lastComponent
|
||||
|
||||
// Calculate relative path within module
|
||||
let relativeToModule: String
|
||||
if source.hasPrefix(module.path + "/") {
|
||||
relativeToModule = String(source.dropFirst(module.path.count + 1))
|
||||
} else if source.hasPrefix("bazel-out/") {
|
||||
// Generated file
|
||||
if let range = source.range(of: module.path + "/") {
|
||||
relativeToModule = String(source[range.upperBound...])
|
||||
} else {
|
||||
relativeToModule = fileName
|
||||
}
|
||||
} else {
|
||||
relativeToModule = fileName
|
||||
}
|
||||
|
||||
// Create symlink
|
||||
let symlinkPath = outputDir + module.path + relativeToModule
|
||||
try symlinkManager.createDirectory(symlinkPath.parent())
|
||||
try symlinkManager.createSymlink(from: Path(source), to: symlinkPath)
|
||||
|
||||
// Create file reference
|
||||
let fileRef = try getOrCreateFileReference(
|
||||
path: relativeToModule,
|
||||
in: moduleGroup,
|
||||
modulePath: module.path,
|
||||
fileName: fileName
|
||||
)
|
||||
|
||||
let isHeader = source.hasSuffix(".h") || source.hasSuffix(".hpp")
|
||||
|
||||
// Determine if this is a public header:
|
||||
// 1. Explicitly in hdrs array
|
||||
// 2. Located in an includes directory
|
||||
let isPublicHeader: Bool
|
||||
if !isHeader {
|
||||
isPublicHeader = false
|
||||
} else if !explicitPublicHeaders.isEmpty {
|
||||
// If hdrs is provided, only those are public
|
||||
isPublicHeader = explicitPublicHeaders.contains(source)
|
||||
} else {
|
||||
// Headers in include directories are public (handles bazel-out paths)
|
||||
isPublicHeader = isInIncludesDirectory(source: source, modulePath: module.path, includes: module.includes)
|
||||
}
|
||||
|
||||
if isPublicHeader {
|
||||
// Skip duplicate header filenames to avoid "multiple commands produce" errors
|
||||
if !seenPublicHeaderNames.contains(fileName) {
|
||||
seenPublicHeaderNames.insert(fileName)
|
||||
publicHeaderRefs.append(fileRef)
|
||||
}
|
||||
} else if !source.hasSuffix(".inc") && !isHeader {
|
||||
// Source files (not headers)
|
||||
sourceRefs.append(fileRef)
|
||||
}
|
||||
// Private headers are not added to any build phase
|
||||
}
|
||||
|
||||
// Build phases
|
||||
var buildPhases: [PBXBuildPhase] = []
|
||||
|
||||
// Sources build phase
|
||||
let sourcesBuildPhase = PBXSourcesBuildPhase(
|
||||
files: sourceRefs.map { ref in
|
||||
let buildFile = PBXBuildFile(file: ref)
|
||||
pbxproj.add(object: buildFile)
|
||||
return buildFile
|
||||
}
|
||||
)
|
||||
pbxproj.add(object: sourcesBuildPhase)
|
||||
buildPhases.append(sourcesBuildPhase)
|
||||
|
||||
// Generate modulemap for ObjC/C++ modules (SPM-style explicit headers)
|
||||
var modulemapPath: Path? = nil
|
||||
if moduleType == .objcLibrary || moduleType == .ccLibrary {
|
||||
modulemapPath = try generateModulemap(for: module)
|
||||
|
||||
// Headers build phase with public headers - needed for ObjC #import to work
|
||||
if !publicHeaderRefs.isEmpty {
|
||||
let headersBuildPhase = PBXHeadersBuildPhase(
|
||||
files: publicHeaderRefs.map { ref in
|
||||
let buildFile = PBXBuildFile(file: ref, settings: ["ATTRIBUTES": ["Public"]])
|
||||
pbxproj.add(object: buildFile)
|
||||
return buildFile
|
||||
}
|
||||
)
|
||||
pbxproj.add(object: headersBuildPhase)
|
||||
buildPhases.append(headersBuildPhase)
|
||||
}
|
||||
}
|
||||
|
||||
// Frameworks build phase
|
||||
let frameworksBuildPhase = PBXFrameworksBuildPhase(files: [])
|
||||
pbxproj.add(object: frameworksBuildPhase)
|
||||
buildPhases.append(frameworksBuildPhase)
|
||||
|
||||
// Create target with custom modulemap if generated
|
||||
let configList = createConfigurationList(for: module, isXCFramework: false, modulemapPath: modulemapPath)
|
||||
|
||||
let target = PBXNativeTarget(
|
||||
name: module.name,
|
||||
buildConfigurationList: configList,
|
||||
buildPhases: buildPhases,
|
||||
productName: module.name,
|
||||
productType: .framework
|
||||
)
|
||||
pbxproj.add(object: target)
|
||||
project.targets.append(target)
|
||||
targetsByName[module.name] = target
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
/// Build a framework target for header-only modules (just headers + modulemap, no sources)
|
||||
private func buildHeaderOnlyFrameworkTarget(for module: ModuleDefinition) throws -> PBXNativeTarget {
|
||||
// Create group for module
|
||||
let moduleGroup = try getOrCreateGroup(for: module.path)
|
||||
|
||||
// Symlink header files and create file references
|
||||
var publicHeaderRefs: [PBXFileReference] = []
|
||||
var seenPublicHeaderNames: Set<String> = [] // Track header filenames to avoid duplicates
|
||||
let allHeaders = module.sources.filter { $0.hasSuffix(".h") || $0.hasSuffix(".hpp") } + (module.hdrs ?? []) + (module.textualHdrs ?? [])
|
||||
|
||||
for source in allHeaders {
|
||||
let sourcePath = Path(source)
|
||||
let fileName = sourcePath.lastComponent
|
||||
let relativeToModule = relativePathInModule(source: source, modulePath: module.path)
|
||||
|
||||
// Create parent group
|
||||
let parentPath = (Path(module.path) + Path(relativeToModule).parent()).string
|
||||
let parentGroup = try getOrCreateGroup(for: parentPath)
|
||||
|
||||
// Create symlink
|
||||
let symlinkPath = outputDir + module.path + relativeToModule
|
||||
try symlinkManager.createDirectory(symlinkPath.parent())
|
||||
try symlinkManager.createSymlink(from: Path(source), to: symlinkPath)
|
||||
|
||||
// Create file reference
|
||||
let fileType = lastKnownFileType(for: fileName)
|
||||
let fileRef = PBXFileReference(
|
||||
sourceTree: .group,
|
||||
name: fileName,
|
||||
lastKnownFileType: fileType,
|
||||
path: fileName
|
||||
)
|
||||
pbxproj.add(object: fileRef)
|
||||
parentGroup.children.append(fileRef)
|
||||
|
||||
// Check if it's a public header (in includes directories)
|
||||
// Use helper that properly handles bazel-out paths
|
||||
let isPublic = isInIncludesDirectory(source: source, modulePath: module.path, includes: module.includes)
|
||||
if isPublic {
|
||||
// Skip duplicate header filenames to avoid "multiple commands produce" errors
|
||||
if !seenPublicHeaderNames.contains(fileName) {
|
||||
seenPublicHeaderNames.insert(fileName)
|
||||
publicHeaderRefs.append(fileRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate modulemap for this header-only module
|
||||
let modulemapPath = try generateModulemap(for: module)
|
||||
|
||||
// Create build phases
|
||||
var buildPhases: [PBXBuildPhase] = []
|
||||
|
||||
// Headers build phase with public headers - needed for ObjC #import to work
|
||||
if !publicHeaderRefs.isEmpty {
|
||||
let headersBuildPhase = PBXHeadersBuildPhase(
|
||||
files: publicHeaderRefs.map { ref in
|
||||
let buildFile = PBXBuildFile(file: ref, settings: ["ATTRIBUTES": ["Public"]])
|
||||
pbxproj.add(object: buildFile)
|
||||
return buildFile
|
||||
}
|
||||
)
|
||||
pbxproj.add(object: headersBuildPhase)
|
||||
buildPhases.append(headersBuildPhase)
|
||||
}
|
||||
|
||||
// Empty frameworks phase (needed for Xcode, but won't link anything)
|
||||
let frameworksBuildPhase = PBXFrameworksBuildPhase(files: [])
|
||||
pbxproj.add(object: frameworksBuildPhase)
|
||||
buildPhases.append(frameworksBuildPhase)
|
||||
|
||||
// Create target with custom modulemap
|
||||
let configList = createConfigurationList(for: module, isXCFramework: false, modulemapPath: modulemapPath)
|
||||
|
||||
let target = PBXNativeTarget(
|
||||
name: module.name,
|
||||
buildConfigurationList: configList,
|
||||
buildPhases: buildPhases,
|
||||
productName: module.name,
|
||||
productType: .framework
|
||||
)
|
||||
pbxproj.add(object: target)
|
||||
project.targets.append(target)
|
||||
targetsByName[module.name] = target
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
/// Collect all transitive dependencies for a module
|
||||
private func collectAllDependencies(for moduleName: String, modules: [String: ModuleDefinition], visited: inout Set<String>) -> Set<String> {
|
||||
guard !visited.contains(moduleName) else { return [] }
|
||||
visited.insert(moduleName)
|
||||
|
||||
guard let module = modules[moduleName], let deps = module.deps else {
|
||||
return []
|
||||
}
|
||||
|
||||
var allDeps = Set(deps)
|
||||
for depName in deps {
|
||||
allDeps.formUnion(collectAllDependencies(for: depName, modules: modules, visited: &visited))
|
||||
}
|
||||
return allDeps
|
||||
}
|
||||
|
||||
func wireUpDependencies(modules: [String: ModuleDefinition]) throws {
|
||||
for (name, module) in modules {
|
||||
guard let target = targetsByName[name],
|
||||
let deps = module.deps else { continue }
|
||||
|
||||
// Find frameworks build phase
|
||||
guard let frameworksPhase = target.buildPhases.compactMap({ $0 as? PBXFrameworksBuildPhase }).first else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Track all frameworks we've added to avoid duplicates
|
||||
var linkedFrameworks: Set<String> = []
|
||||
// Track library search paths for static libraries
|
||||
var staticLibSearchPaths: Set<String> = []
|
||||
|
||||
// Add target dependency (only for direct deps)
|
||||
for depName in deps {
|
||||
if let depTarget = targetsByName[depName] {
|
||||
let dependency = PBXTargetDependency(target: depTarget)
|
||||
pbxproj.add(object: dependency)
|
||||
target.dependencies.append(dependency)
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all transitive dependencies to link
|
||||
var visited = Set<String>()
|
||||
let allDeps = collectAllDependencies(for: name, modules: modules, visited: &visited)
|
||||
|
||||
// Collect header paths from ALL dependencies (direct and transitive)
|
||||
var depHeaderPaths: [String] = []
|
||||
for depName in allDeps {
|
||||
if let depModule = modules[depName] {
|
||||
depHeaderPaths.append(contentsOf: exportedHeaderPaths(for: depModule))
|
||||
}
|
||||
}
|
||||
|
||||
// Link all dependencies (direct and transitive) that have targets
|
||||
for depName in allDeps {
|
||||
// Skip if already linked
|
||||
guard !linkedFrameworks.contains(depName) else { continue }
|
||||
|
||||
// Check if this is a static library module - link .a files directly
|
||||
if isStaticLibraryModule(depName) {
|
||||
// Link static libraries directly
|
||||
for libPath in getStaticLibraries(for: depName) {
|
||||
let libName = Path(libPath).lastComponent
|
||||
// Create an absolute path to the project root then to the static library
|
||||
// SRCROOT is xcode-files, so we need to go up one level to get to telegram-ios
|
||||
let projectRoot = outputDir.parent()
|
||||
let absoluteLibPath = (projectRoot + libPath).string
|
||||
let libRef = PBXFileReference(
|
||||
sourceTree: .absolute,
|
||||
name: libName,
|
||||
lastKnownFileType: "archive.ar",
|
||||
path: absoluteLibPath
|
||||
)
|
||||
pbxproj.add(object: libRef)
|
||||
let buildFile = PBXBuildFile(file: libRef)
|
||||
pbxproj.add(object: buildFile)
|
||||
frameworksPhase.files?.append(buildFile)
|
||||
|
||||
// Add the library's directory to LIBRARY_SEARCH_PATHS
|
||||
let libDir = Path(absoluteLibPath).parent().string
|
||||
if !staticLibSearchPaths.contains(libDir) {
|
||||
staticLibSearchPaths.insert(libDir)
|
||||
}
|
||||
}
|
||||
linkedFrameworks.insert(depName)
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip header-only modules (no framework to link)
|
||||
if isHeaderOnlyModule(depName) { continue }
|
||||
|
||||
// Regular framework dependency
|
||||
guard targetsByName[depName] != nil else { continue }
|
||||
|
||||
linkedFrameworks.insert(depName)
|
||||
let frameworkRef = PBXFileReference(
|
||||
sourceTree: .buildProductsDir,
|
||||
name: "\(depName).framework",
|
||||
lastKnownFileType: "wrapper.framework",
|
||||
path: "\(depName).framework"
|
||||
)
|
||||
pbxproj.add(object: frameworkRef)
|
||||
let buildFile = PBXBuildFile(file: frameworkRef)
|
||||
pbxproj.add(object: buildFile)
|
||||
frameworksPhase.files?.append(buildFile)
|
||||
}
|
||||
|
||||
// Update build configurations with dependency paths
|
||||
if !depHeaderPaths.isEmpty || !deps.isEmpty || !staticLibSearchPaths.isEmpty {
|
||||
for config in target.buildConfigurationList?.buildConfigurations ?? [] {
|
||||
// Add header search paths
|
||||
if !depHeaderPaths.isEmpty {
|
||||
let existing = config.buildSettings["HEADER_SEARCH_PATHS"] as? String ?? "$(inherited)"
|
||||
config.buildSettings["HEADER_SEARCH_PATHS"] = existing + " " + depHeaderPaths.joined(separator: " ")
|
||||
}
|
||||
// Ensure framework and module search paths include built products
|
||||
config.buildSettings["FRAMEWORK_SEARCH_PATHS"] = "$(inherited) $(BUILT_PRODUCTS_DIR)"
|
||||
config.buildSettings["SWIFT_INCLUDE_PATHS"] = "$(inherited) $(BUILT_PRODUCTS_DIR)"
|
||||
// Add library search paths for static libraries
|
||||
if !staticLibSearchPaths.isEmpty {
|
||||
let existing = config.buildSettings["LIBRARY_SEARCH_PATHS"] as? String ?? "$(inherited)"
|
||||
config.buildSettings["LIBRARY_SEARCH_PATHS"] = existing + " " + staticLibSearchPaths.sorted().joined(separator: " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect SDK frameworks from this module and all transitive dependencies
|
||||
var allSdkFrameworks: Set<String> = []
|
||||
if let sdkFrameworks = module.sdkFrameworks {
|
||||
allSdkFrameworks.formUnion(sdkFrameworks)
|
||||
}
|
||||
for depName in allDeps {
|
||||
if let depModule = modules[depName], let depFrameworks = depModule.sdkFrameworks {
|
||||
allSdkFrameworks.formUnion(depFrameworks)
|
||||
}
|
||||
}
|
||||
|
||||
// Add SDK frameworks
|
||||
for framework in allSdkFrameworks {
|
||||
let fileRef = PBXFileReference(
|
||||
sourceTree: .sdkRoot,
|
||||
name: "\(framework).framework",
|
||||
lastKnownFileType: "wrapper.framework",
|
||||
path: "System/Library/Frameworks/\(framework).framework"
|
||||
)
|
||||
pbxproj.add(object: fileRef)
|
||||
let buildFile = PBXBuildFile(file: fileRef)
|
||||
pbxproj.add(object: buildFile)
|
||||
frameworksPhase.files?.append(buildFile)
|
||||
}
|
||||
|
||||
// Collect SDK dylibs from this module and all transitive dependencies
|
||||
var allSdkDylibs: Set<String> = []
|
||||
if let sdkDylibs = module.sdkDylibs {
|
||||
allSdkDylibs.formUnion(sdkDylibs)
|
||||
}
|
||||
for depName in allDeps {
|
||||
if let depModule = modules[depName], let depDylibs = depModule.sdkDylibs {
|
||||
allSdkDylibs.formUnion(depDylibs)
|
||||
}
|
||||
}
|
||||
|
||||
// Add SDK dylibs (system libraries like libz, libiconv, etc.)
|
||||
for dylib in allSdkDylibs {
|
||||
// Clean up the library name - remove 'lib' prefix if present
|
||||
let libName = dylib.hasPrefix("lib") ? String(dylib.dropFirst(3)) : dylib
|
||||
let fileRef = PBXFileReference(
|
||||
sourceTree: .sdkRoot,
|
||||
name: "lib\(libName).tbd",
|
||||
lastKnownFileType: "sourcecode.text-based-dylib-definition",
|
||||
path: "usr/lib/lib\(libName).tbd"
|
||||
)
|
||||
pbxproj.add(object: fileRef)
|
||||
let buildFile = PBXBuildFile(file: fileRef)
|
||||
pbxproj.add(object: buildFile)
|
||||
frameworksPhase.files?.append(buildFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the header search paths that this module exports to its dependents
|
||||
private func exportedHeaderPaths(for module: ModuleDefinition) -> [String] {
|
||||
var paths: [String] = []
|
||||
if let includes = module.includes, !includes.isEmpty {
|
||||
for inc in includes {
|
||||
if inc == "." {
|
||||
paths.append("$(SRCROOT)/\(module.path)")
|
||||
} else {
|
||||
paths.append("$(SRCROOT)/\(module.path)/\(inc)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No includes specified, export module's own path
|
||||
paths.append("$(SRCROOT)/\(module.path)")
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func getTarget(named name: String) -> PBXNativeTarget? {
|
||||
return targetsByName[name]
|
||||
}
|
||||
|
||||
|
||||
private func createConfigurationList(for module: ModuleDefinition, isXCFramework: Bool, modulemapPath: Path? = nil) -> XCConfigurationList {
|
||||
let debugSettings = createBuildSettings(for: module, isDebug: true, isXCFramework: isXCFramework, modulemapPath: modulemapPath)
|
||||
let releaseSettings = createBuildSettings(for: module, isDebug: false, isXCFramework: isXCFramework, modulemapPath: modulemapPath)
|
||||
|
||||
let debugConfig = XCBuildConfiguration(name: "Debug", buildSettings: debugSettings)
|
||||
let releaseConfig = XCBuildConfiguration(name: "Release", buildSettings: releaseSettings)
|
||||
|
||||
pbxproj.add(object: debugConfig)
|
||||
pbxproj.add(object: releaseConfig)
|
||||
|
||||
let configList = XCConfigurationList(
|
||||
buildConfigurations: [debugConfig, releaseConfig],
|
||||
defaultConfigurationName: "Release"
|
||||
)
|
||||
pbxproj.add(object: configList)
|
||||
|
||||
return configList
|
||||
}
|
||||
|
||||
private func createBuildSettings(for module: ModuleDefinition, isDebug: Bool, isXCFramework: Bool, modulemapPath: Path? = nil) -> BuildSettings {
|
||||
var settings: BuildSettings = [
|
||||
"PRODUCT_NAME": "$(TARGET_NAME)",
|
||||
"PRODUCT_BUNDLE_IDENTIFIER": "org.telegram.\(module.name)",
|
||||
"INFOPLIST_FILE": "",
|
||||
"SKIP_INSTALL": "YES",
|
||||
"GENERATE_INFOPLIST_FILE": "YES",
|
||||
]
|
||||
|
||||
let moduleType = ModuleType(from: module)
|
||||
|
||||
// Swift settings
|
||||
if moduleType == .swiftLibrary {
|
||||
settings["SWIFT_VERSION"] = "5.0"
|
||||
settings["DEFINES_MODULE"] = "YES"
|
||||
|
||||
if let copts = module.copts, !copts.isEmpty {
|
||||
let filtered = copts.filter { !$0.hasPrefix("-warnings") }
|
||||
if !filtered.isEmpty {
|
||||
settings["OTHER_SWIFT_FLAGS"] = "$(inherited) " + filtered.joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
||||
if let defines = module.defines, !defines.isEmpty {
|
||||
settings["SWIFT_ACTIVE_COMPILATION_CONDITIONS"] = "$(inherited) " + defines.joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
||||
// C/ObjC settings
|
||||
if moduleType == .objcLibrary || moduleType == .ccLibrary {
|
||||
// Always suppress deprecated warnings (e.g., OSSpinLock) and don't treat warnings as errors
|
||||
var cflags = ["-Wno-deprecated-declarations"]
|
||||
if let copts = module.copts, !copts.isEmpty {
|
||||
let filtered = copts.filter { !$0.hasPrefix("-warnings") && !$0.hasPrefix("-W") }
|
||||
cflags.append(contentsOf: filtered)
|
||||
}
|
||||
settings["OTHER_CFLAGS"] = "$(inherited) " + cflags.joined(separator: " ")
|
||||
settings["GCC_TREAT_WARNINGS_AS_ERRORS"] = "NO"
|
||||
|
||||
if let cxxopts = module.cxxopts, !cxxopts.isEmpty {
|
||||
let filtered = cxxopts.filter { !$0.hasPrefix("-std=") }
|
||||
if !filtered.isEmpty {
|
||||
settings["OTHER_CPLUSPLUSFLAGS"] = "$(inherited) " + filtered.joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
||||
if let defines = module.defines, !defines.isEmpty {
|
||||
settings["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) " + defines.joined(separator: " ")
|
||||
}
|
||||
|
||||
// Always include module's own path for header search
|
||||
var headerPaths = ["$(SRCROOT)/\(module.path)"]
|
||||
if let includes = module.includes {
|
||||
for inc in includes where inc != "." {
|
||||
headerPaths.append("$(SRCROOT)/\(module.path)/\(inc)")
|
||||
}
|
||||
}
|
||||
settings["HEADER_SEARCH_PATHS"] = "$(inherited) " + headerPaths.joined(separator: " ")
|
||||
|
||||
// Use custom modulemap if provided (SPM-style)
|
||||
if let modmap = modulemapPath {
|
||||
// Get relative path from SRCROOT
|
||||
let relativePath = modmap.string.replacingOccurrences(of: outputDir.string + "/", with: "")
|
||||
settings["MODULEMAP_FILE"] = "$(SRCROOT)/\(relativePath)"
|
||||
}
|
||||
settings["DEFINES_MODULE"] = "YES"
|
||||
}
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
private func getOrCreateGroup(for path: String) throws -> PBXGroup {
|
||||
if let existing = groupsByPath[path] {
|
||||
return existing
|
||||
}
|
||||
|
||||
let components = path.split(separator: "/").map(String.init)
|
||||
var currentGroup = mainGroup
|
||||
var currentPath = ""
|
||||
|
||||
for component in components {
|
||||
currentPath = currentPath.isEmpty ? component : currentPath + "/" + component
|
||||
|
||||
if let existing = groupsByPath[currentPath] {
|
||||
currentGroup = existing
|
||||
} else {
|
||||
let newGroup = PBXGroup(children: [], sourceTree: .group, name: component, path: component)
|
||||
pbxproj.add(object: newGroup)
|
||||
currentGroup.children.append(newGroup)
|
||||
groupsByPath[currentPath] = newGroup
|
||||
currentGroup = newGroup
|
||||
}
|
||||
}
|
||||
|
||||
return currentGroup
|
||||
}
|
||||
|
||||
private func getOrCreateFileReference(path: String, in group: PBXGroup, modulePath: String, fileName: String) throws -> PBXFileReference {
|
||||
let pathComponents = path.split(separator: "/").map(String.init)
|
||||
|
||||
var currentGroup = group
|
||||
var currentPath = modulePath
|
||||
|
||||
// Navigate/create intermediate groups
|
||||
for component in pathComponents.dropLast() {
|
||||
currentPath = currentPath + "/" + component
|
||||
if let existing = groupsByPath[currentPath] {
|
||||
currentGroup = existing
|
||||
} else {
|
||||
let newGroup = PBXGroup(children: [], sourceTree: .group, name: component, path: component)
|
||||
pbxproj.add(object: newGroup)
|
||||
currentGroup.children.append(newGroup)
|
||||
groupsByPath[currentPath] = newGroup
|
||||
currentGroup = newGroup
|
||||
}
|
||||
}
|
||||
|
||||
// Create file reference
|
||||
let fileType = lastKnownFileType(for: fileName)
|
||||
let fileRef = PBXFileReference(
|
||||
sourceTree: .group,
|
||||
name: fileName,
|
||||
lastKnownFileType: fileType,
|
||||
path: fileName
|
||||
)
|
||||
pbxproj.add(object: fileRef)
|
||||
currentGroup.children.append(fileRef)
|
||||
|
||||
return fileRef
|
||||
}
|
||||
|
||||
private func lastKnownFileType(for fileName: String) -> String {
|
||||
let ext = (fileName as NSString).pathExtension.lowercased()
|
||||
switch ext {
|
||||
case "swift": return "sourcecode.swift"
|
||||
case "m": return "sourcecode.c.objc"
|
||||
case "mm": return "sourcecode.cpp.objcpp"
|
||||
case "c": return "sourcecode.c.c"
|
||||
case "cc", "cpp", "cxx": return "sourcecode.cpp.cpp"
|
||||
case "h": return "sourcecode.c.h"
|
||||
case "hpp": return "sourcecode.cpp.h"
|
||||
case "metal": return "sourcecode.metal"
|
||||
case "json": return "text.json"
|
||||
case "plist": return "text.plist.xml"
|
||||
default: return "text"
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the relative path within a module from a source path
|
||||
/// Handles both regular paths (submodules/Foo/...) and bazel-out paths (bazel-out/.../bin/submodules/Foo/...)
|
||||
private func relativePathInModule(source: String, modulePath: String) -> String {
|
||||
if source.hasPrefix(modulePath + "/") {
|
||||
return String(source.dropFirst(modulePath.count + 1))
|
||||
} else if source.hasPrefix("bazel-out/") {
|
||||
// Generated file - extract path after the module path portion
|
||||
if let range = source.range(of: modulePath + "/") {
|
||||
return String(source[range.upperBound...])
|
||||
} else {
|
||||
return Path(source).lastComponent
|
||||
}
|
||||
} else {
|
||||
return Path(source).lastComponent
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a source file is in one of the includes directories
|
||||
private func isInIncludesDirectory(source: String, modulePath: String, includes: [String]?) -> Bool {
|
||||
guard let includes = includes, !includes.isEmpty else {
|
||||
return false
|
||||
}
|
||||
let relative = relativePathInModule(source: source, modulePath: modulePath)
|
||||
return includes.contains { inc in
|
||||
if inc == "." {
|
||||
return true // All files in module are public
|
||||
} else {
|
||||
return relative.hasPrefix(inc + "/") || relative == inc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an explicit module.modulemap for ObjC/C++ modules (SPM-style)
|
||||
/// Returns the path to the modulemap, or nil if no public headers
|
||||
func generateModulemap(for module: ModuleDefinition) throws -> Path? {
|
||||
let moduleType = ModuleType(from: module)
|
||||
guard moduleType == .objcLibrary || moduleType == .ccLibrary else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Determine public header prefix from includes
|
||||
let publicHeaderPrefix: String
|
||||
if let includes = module.includes, !includes.isEmpty {
|
||||
let firstInclude = includes[0]
|
||||
publicHeaderPrefix = firstInclude == "." ? "" : firstInclude
|
||||
} else {
|
||||
publicHeaderPrefix = ""
|
||||
}
|
||||
|
||||
// Determine public headers
|
||||
let explicitPublicHeaders = Set(module.hdrs ?? [])
|
||||
|
||||
let allFiles = module.sources + (module.hdrs ?? []) + (module.textualHdrs ?? [])
|
||||
let allHeaders = allFiles.filter { $0.hasSuffix(".h") || $0.hasSuffix(".hpp") }
|
||||
|
||||
// Determine which headers are public
|
||||
let publicHeaders: [String]
|
||||
if !explicitPublicHeaders.isEmpty {
|
||||
publicHeaders = allHeaders.filter { explicitPublicHeaders.contains($0) }
|
||||
} else if module.includes != nil && !module.includes!.isEmpty {
|
||||
// Use helper that properly handles bazel-out paths
|
||||
publicHeaders = allHeaders.filter { header in
|
||||
isInIncludesDirectory(source: header, modulePath: module.path, includes: module.includes)
|
||||
}
|
||||
} else {
|
||||
publicHeaders = []
|
||||
}
|
||||
|
||||
guard !publicHeaders.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate explicit modulemap content (SPM-style)
|
||||
var content = "// module.modulemap for \(module.name)\n"
|
||||
content += "// Auto-generated - do not edit\n\n"
|
||||
content += "module \(module.name) {\n"
|
||||
|
||||
for header in publicHeaders {
|
||||
// Calculate the symlinked path - matches the symlink creation in buildFrameworkTarget
|
||||
// The symlink is at: outputDir + module.path + relativeToModule
|
||||
let relativeToModule = relativePathInModule(source: header, modulePath: module.path)
|
||||
let symlinkPath = outputDir + module.path + relativeToModule
|
||||
content += " header \"\(symlinkPath.string)\"\n"
|
||||
}
|
||||
|
||||
content += " export *\n"
|
||||
content += "}\n"
|
||||
|
||||
// Write modulemap to the public headers directory
|
||||
let modulemapDir: Path
|
||||
if !publicHeaderPrefix.isEmpty {
|
||||
modulemapDir = outputDir + module.path + publicHeaderPrefix
|
||||
} else {
|
||||
modulemapDir = outputDir + module.path
|
||||
}
|
||||
let modulemapPath = modulemapDir + "module.modulemap"
|
||||
|
||||
try modulemapDir.mkpath()
|
||||
try modulemapPath.write(content)
|
||||
|
||||
// Track this file so it doesn't get cleaned up
|
||||
symlinkManager.markFile(modulemapPath)
|
||||
|
||||
return modulemapPath
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Foundation
|
||||
import ArgumentParser
|
||||
import PathKit
|
||||
|
||||
struct MakeProject: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(
|
||||
commandName: "MakeProject",
|
||||
abstract: "Generate Xcode project from Bazel module definitions"
|
||||
)
|
||||
|
||||
@Option(name: .long, help: "Path to modules JSON file")
|
||||
var modulesJson: String = "bazel-bin/Telegram/spm_build_root_modules.json"
|
||||
|
||||
@Option(name: .long, help: "Output directory for generated project")
|
||||
var output: String = "xcode-files"
|
||||
|
||||
func run() throws {
|
||||
// Determine project root (where we find the modules JSON)
|
||||
let currentDir = Path.current
|
||||
var projectRoot = currentDir
|
||||
|
||||
// Walk up to find project root (contains bazel-bin or the modules file)
|
||||
var searchDir = currentDir
|
||||
for _ in 0..<5 {
|
||||
if (searchDir + modulesJson).exists {
|
||||
projectRoot = searchDir
|
||||
break
|
||||
}
|
||||
searchDir = searchDir.parent()
|
||||
}
|
||||
|
||||
let modulesPath = projectRoot + modulesJson
|
||||
let outputDir = projectRoot + output
|
||||
|
||||
guard modulesPath.exists else {
|
||||
print("Error: Modules JSON not found at \(modulesPath)")
|
||||
print("Run 'bazel build //Telegram:spm_build_root' first")
|
||||
throw ExitCode.failure
|
||||
}
|
||||
|
||||
let generator = ProjectGenerator(
|
||||
modulesPath: modulesPath,
|
||||
outputDir: outputDir,
|
||||
projectRoot: projectRoot
|
||||
)
|
||||
|
||||
try generator.generate()
|
||||
}
|
||||
}
|
||||
|
||||
MakeProject.main()
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"bundle_id": "ph.telegra.Telegraph",
|
||||
"api_id": "8",
|
||||
"api_hash": "YOUR_API_HASH",
|
||||
"api_hash": "7245de8e747a0d6fbe11f7cc14fcc0bb",
|
||||
"team_id": "C67CF9S4VU",
|
||||
"app_center_id": "4c816ed0-df83-423c-846b-a0a8467dc7d2",
|
||||
"is_internal_build": "false",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"bundle_id": "ph.telegra.Telegraph",
|
||||
"api_id": "8",
|
||||
"api_hash": "YOUR_API_HASH",
|
||||
"api_hash": "7245de8e747a0d6fbe11f7cc14fcc0bb",
|
||||
"team_id": "C67CF9S4VU",
|
||||
"app_center_id": "0",
|
||||
"is_internal_build": "false",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
telegram_bundle_id = "ph.telegra.Telegraph"
|
||||
telegram_api_id = "8"
|
||||
telegram_api_hash = "YOUR_API_HASH"
|
||||
telegram_api_hash = "7245de8e747a0d6fbe11f7cc14fcc0bb"
|
||||
telegram_team_id = "C67CF9S4VU"
|
||||
telegram_app_center_id = "0"
|
||||
telegram_is_internal_build = "false"
|
||||
|
||||
@@ -7,7 +7,7 @@ export DISTRIBUTION_CODE_SIGN_IDENTITY="iPhone Distribution: Digital Fortress LL
|
||||
export DEVELOPMENT_TEAM="C67CF9S4VU"
|
||||
|
||||
export API_ID="8"
|
||||
export API_HASH="YOUR_API_HASH"
|
||||
export API_HASH="7245de8e747a0d6fbe11f7cc14fcc0bb"
|
||||
|
||||
export BUNDLE_ID="ph.telegra.Telegraph"
|
||||
export APP_CENTER_ID="0"
|
||||
|
||||
@@ -1376,7 +1376,7 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeBotPreviewEditorScreen(context: AccountContext, source: Any?, target: Stories.PendingTarget, transitionArguments: (UIView, CGRect, UIImage?)?, transitionOut: @escaping () -> BotPreviewEditorTransitionOut?, externalState: MediaEditorTransitionOutExternalState, completion: @escaping (MediaEditorScreenResult, @escaping (@escaping () -> Void) -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
||||
func makeStickerEditorScreen(context: AccountContext, source: Any?, intro: Bool, transitionArguments: (UIView, CGRect, UIImage?)?, completion: @escaping (TelegramMediaFile, [String], @escaping () -> Void) -> Void, cancelled: @escaping () -> Void) -> ViewController
|
||||
func makeStickerMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||
func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> ViewController
|
||||
func makeAvatarMediaPickerScreen(context: AccountContext, getSourceRect: @escaping () -> CGRect?, canDelete: Bool, performDelete: @escaping () -> Void, completion: @escaping (Any?, UIView?, CGRect, UIImage?, Bool, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, dismissed: @escaping () -> Void) -> (ViewController?, Any?)
|
||||
func makeStoryMediaPickerScreen(context: AccountContext, isDark: Bool, forCollage: Bool, selectionLimit: Int?, getSourceRect: @escaping () -> CGRect, completion: @escaping (Any, UIView, CGRect, UIImage?, @escaping (Bool?) -> (UIView, CGRect)?, @escaping () -> Void) -> Void, multipleCompletion: @escaping ([Any], Bool) -> Void, dismissed: @escaping () -> Void, groupsPresented: @escaping () -> Void) -> ViewController
|
||||
func makeStickerPickerScreen(context: AccountContext, inputData: Promise<StickerPickerInput>, completion: @escaping (FileMediaReference) -> Void) -> ViewController
|
||||
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||
@@ -1407,10 +1407,10 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController
|
||||
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
|
||||
func makeGiftAuctionBidScreen(context: AccountContext, toPeerId: EnginePeer.Id, text: String?, entities: [MessageTextEntity]?, hideName: Bool, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?) -> ViewController
|
||||
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController
|
||||
func makeGiftAuctionViewScreen(context: AccountContext, auctionContext: GiftAuctionContext, peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController
|
||||
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> ViewController
|
||||
func makeGiftOfferScreen(context: AccountContext, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
|
||||
func makeGiftUpgradeVariantsPreviewScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute]) -> ViewController
|
||||
func makeGiftOfferScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, gift: StarGift.UniqueGift, peer: EnginePeer, amount: CurrencyAmount, commit: @escaping () -> Void) -> ViewController
|
||||
func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController
|
||||
func makeGiftAuctionWearPreviewScreen(context: AccountContext, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?, attributes: [StarGift.UniqueGift.Attribute], completion: @escaping () -> Void) -> ViewController
|
||||
func makeGiftDemoScreen(context: AccountContext) -> ViewController
|
||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||
@@ -1428,6 +1428,8 @@ public protocol SharedAccountContext: AnyObject {
|
||||
func makeGalleryController(context: AccountContext, source: GalleryControllerItemSource, streamSingleVideo: Bool, isPreview: Bool) -> ViewController
|
||||
func makeAccountFreezeInfoScreen(context: AccountContext) -> ViewController
|
||||
func makeSendInviteLinkScreen(context: AccountContext, subject: SendInviteLinkScreenSubject, peers: [TelegramForbiddenInvitePeer], theme: PresentationTheme?) -> ViewController
|
||||
func makeCocoonInfoScreen(context: AccountContext) -> ViewController
|
||||
func makeLinkEditController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, text: String, link: String?, apply: @escaping (String?) -> Void) -> ViewController
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
func makePostSuggestionsSettingsScreen(context: AccountContext, peerId: EnginePeer.Id) async -> ViewController
|
||||
@@ -1678,7 +1680,7 @@ public struct StarsSubscriptionConfiguration {
|
||||
return StarsSubscriptionConfiguration(
|
||||
maxFee: 2500,
|
||||
usdWithdrawRate: 1200,
|
||||
tonUsdRate: 0,
|
||||
tonUsdRate: 1.0,
|
||||
paidMessageMaxAmount: 10000,
|
||||
paidMessageCommissionPermille: 850,
|
||||
paidMessagesAvailable: false,
|
||||
@@ -1698,7 +1700,7 @@ public struct StarsSubscriptionConfiguration {
|
||||
|
||||
public let maxFee: Int64
|
||||
public let usdWithdrawRate: Int64
|
||||
public let tonUsdRate: Int64
|
||||
public let tonUsdRate: Double
|
||||
public let paidMessageMaxAmount: Int64
|
||||
public let paidMessageCommissionPermille: Int32
|
||||
public let paidMessagesAvailable: Bool
|
||||
@@ -1717,7 +1719,7 @@ public struct StarsSubscriptionConfiguration {
|
||||
fileprivate init(
|
||||
maxFee: Int64,
|
||||
usdWithdrawRate: Int64,
|
||||
tonUsdRate: Int64,
|
||||
tonUsdRate: Double,
|
||||
paidMessageMaxAmount: Int64,
|
||||
paidMessageCommissionPermille: Int32,
|
||||
paidMessagesAvailable: Bool,
|
||||
@@ -1756,7 +1758,7 @@ public struct StarsSubscriptionConfiguration {
|
||||
if let data = appConfiguration.data {
|
||||
let maxFee = (data["stars_subscription_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.maxFee
|
||||
let usdWithdrawRate = (data["stars_usd_withdraw_rate_x1000"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.usdWithdrawRate
|
||||
let tonUsdRate = (data["ton_usd_rate"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.tonUsdRate
|
||||
let tonUsdRate = (data["ton_usd_rate"] as? Double) ?? StarsSubscriptionConfiguration.defaultValue.tonUsdRate
|
||||
let paidMessageMaxAmount = (data["stars_paid_message_amount_max"] as? Double).flatMap(Int64.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageMaxAmount
|
||||
let paidMessageCommissionPermille = (data["stars_paid_message_commission_permille"] as? Double).flatMap(Int32.init) ?? StarsSubscriptionConfiguration.defaultValue.paidMessageCommissionPermille
|
||||
let paidMessagesAvailable = (data["stars_paid_messages_available"] as? Bool) ?? StarsSubscriptionConfiguration.defaultValue.paidMessagesAvailable
|
||||
|
||||
@@ -1216,7 +1216,7 @@ public enum ChatHistoryListDisplayHeaders {
|
||||
|
||||
public enum ChatHistoryListMode: Equatable {
|
||||
case bubbles
|
||||
case list(search: Bool, reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool)
|
||||
case list(reversed: Bool, reverseGroups: Bool, displayHeaders: ChatHistoryListDisplayHeaders, hintLinks: Bool, isGlobalSearch: Bool)
|
||||
}
|
||||
|
||||
public protocol ChatControllerInteractionProtocol: AnyObject {
|
||||
|
||||
@@ -4,8 +4,9 @@ import TelegramCore
|
||||
import TelegramUIPreferences
|
||||
import AccountContext
|
||||
|
||||
public let maximumNumberOfAccounts = 3
|
||||
public let maximumPremiumNumberOfAccounts = 4
|
||||
// GHOSTGRAM: Unlimited accounts bypass - always allow up to 10 accounts
|
||||
public let maximumNumberOfAccounts = 10
|
||||
public let maximumPremiumNumberOfAccounts = 10
|
||||
|
||||
public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, EnginePeer)?, [(AccountContext, EnginePeer, Int32)]), NoError> {
|
||||
let sharedContext = context.sharedContext
|
||||
|
||||
@@ -73,7 +73,7 @@ public final class AdInfoScreen: ViewController {
|
||||
|
||||
self.titleNode = ImmediateTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 1
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: NavigationBar.titleFont, textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor)
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.presentationData.strings.SponsoredMessageInfoScreen_Title, font: Font.with(size: 17.0, design: .regular, weight: .semibold, traits: [.monospacedNumbers]), textColor: self.presentationData.theme.rootController.navigationBar.primaryTextColor)
|
||||
|
||||
self.scrollNode = ASScrollNode()
|
||||
self.scrollNode.view.showsVerticalScrollIndicator = true
|
||||
|
||||
@@ -44,6 +44,8 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/MinimizedContainer",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/LiquidLens",
|
||||
"//submodules/TelegramUI/Components/TabSelectionRecognizer",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -24,6 +24,8 @@ import LegacyMessageInputPanelInputView
|
||||
import ReactionSelectionNode
|
||||
import TopMessageReactions
|
||||
import GlassBackgroundComponent
|
||||
import LiquidLens
|
||||
import TabSelectionRecognizer
|
||||
|
||||
private let legacyButtonSize = CGSize(width: 88.0, height: 49.0)
|
||||
private let glassButtonSize = CGSize(width: 72.0, height: 62.0)
|
||||
@@ -887,7 +889,7 @@ private final class MainButtonNode: HighlightTrackingButtonNode {
|
||||
}
|
||||
}
|
||||
|
||||
final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate, ASGestureRecognizerDelegate {
|
||||
enum Style {
|
||||
case glass
|
||||
case legacy
|
||||
@@ -912,12 +914,22 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
private let containerNode: ASDisplayNode
|
||||
private var backgroundView: GlassBackgroundView?
|
||||
private var liquidLensView: LiquidLensView?
|
||||
private let backgroundNode: NavigationBackgroundNode
|
||||
private let scrollNode: ASScrollNode
|
||||
private let separatorNode: ASDisplayNode
|
||||
|
||||
private let selectionNode: ASImageNode
|
||||
private var buttonViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
|
||||
private var itemsContainer = UIView()
|
||||
private var itemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
private var selectedItemsContainer = UIView()
|
||||
private var selectedItemViews: [AnyHashable: ComponentHostView<Empty>] = [:]
|
||||
private var itemSizes: [AnyHashable: CGSize] = [:]
|
||||
|
||||
private var tabSelectionRecognizer: TabSelectionRecognizer?
|
||||
private var selectionGestureState: (startX: CGFloat, currentX: CGFloat, itemId: AnyHashable, isLifted: Bool)?
|
||||
private var lensIsLifted = false
|
||||
|
||||
private var textInputPanelNode: AttachmentTextInputPanelNode?
|
||||
private var progressNode: LoadingProgressNode?
|
||||
@@ -971,7 +983,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
self.makeEntityInputView = makeEntityInputView
|
||||
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
|
||||
self.presentationInterfaceState = ChatPresentationInterfaceState(chatWallpaper: .builtin(WallpaperSettings()), theme: self.presentationData.theme, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameDisplayOrder: self.presentationData.nameDisplayOrder, limitsConfiguration: self.context.currentLimitsConfiguration.with { $0 }, fontSize: self.presentationData.chatFontSize, bubbleCorners: self.presentationData.chatBubbleCorners, accountPeerId: self.context.account.peerId, mode: .standard(.default), chatLocation: chatLocation ?? .peer(id: context.account.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, threadData: nil, isGeneralThreadClosed: nil, replyMessage: nil, accountPeerColor: nil, businessIntro: nil)
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
self.containerNode.clipsToBounds = false
|
||||
@@ -987,7 +999,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
|
||||
self.mainButtonNode = MainButtonNode()
|
||||
self.secondaryButtonNode = MainButtonNode()
|
||||
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
@@ -995,7 +1007,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
switch style {
|
||||
case .glass:
|
||||
self.scrollNode.cornerRadius = glassButtonSize.height * 0.5
|
||||
self.scrollNode.addSubnode(self.selectionNode)
|
||||
case .legacy:
|
||||
self.containerNode.addSubnode(self.backgroundNode)
|
||||
self.containerNode.addSubnode(self.separatorNode)
|
||||
@@ -1135,7 +1146,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = chatTextLinkEditController(sharedContext: strongSelf.context.sharedContext, updatedPresentationData: (presentationData, .never()), account: strongSelf.context.account, text: text?.string ?? "", link: link, apply: { [weak self] link in
|
||||
let controller = chatTextLinkEditController(context: strongSelf.context, updatedPresentationData: (presentationData, .never()), text: text?.string ?? "", link: link, apply: { [weak self] link in
|
||||
if let strongSelf = self, let inputMode = inputMode, let selectionRange = selectionRange {
|
||||
if let link = link {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, { state in
|
||||
@@ -1419,6 +1430,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
self.view.accessibilityTraits = .tabBar
|
||||
}
|
||||
|
||||
func requestLayout(transition: ContainedViewLayoutTransition) {
|
||||
if let layout = self.validLayout {
|
||||
let _ = self.update(layout: layout, buttons: self.buttons, isSelecting: self.isSelecting, selectionCount: self.selectionCount, elevateProgress: self.elevateProgress, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func mainButtonPressed() {
|
||||
self.onMainButtonPressed()
|
||||
}
|
||||
@@ -1473,6 +1490,105 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func item(at point: CGPoint) -> AnyHashable? {
|
||||
let contentOffset = self.scrollNode.view.contentOffset.x
|
||||
let point = point.offsetBy(dx: contentOffset, dy: 0.0)
|
||||
var closestItem: (AnyHashable, CGFloat)?
|
||||
for (id, itemView) in self.itemViews {
|
||||
if itemView.frame.contains(point) {
|
||||
return id
|
||||
} else {
|
||||
let distance = abs(point.x - itemView.center.x)
|
||||
if let closestItemValue = closestItem {
|
||||
if closestItemValue.1 > distance {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
} else {
|
||||
closestItem = (id, distance)
|
||||
}
|
||||
}
|
||||
}
|
||||
return closestItem?.0
|
||||
}
|
||||
|
||||
@objc private func onTabSelectionGesture(_ recognizer: TabSelectionRecognizer) {
|
||||
guard let liquidLensView = self.liquidLensView else {
|
||||
return
|
||||
}
|
||||
let location = recognizer.location(in: liquidLensView.contentView)
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if let itemId = self.item(at: location), let itemView = self.itemViews[itemId] {
|
||||
let startX = itemView.frame.minX
|
||||
self.selectionGestureState = (startX, startX, itemId, !self.scrollNode.view.isScrollEnabled)
|
||||
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
case .changed:
|
||||
if var selectionGestureState = self.selectionGestureState {
|
||||
selectionGestureState.currentX = selectionGestureState.startX + recognizer.translation(in: self.view).x
|
||||
if let itemId = self.item(at: location) {
|
||||
selectionGestureState.itemId = itemId
|
||||
}
|
||||
self.selectionGestureState = selectionGestureState
|
||||
self.requestLayout(transition: .immediate)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let selectionGestureState = self.selectionGestureState {
|
||||
if !selectionGestureState.isLifted {
|
||||
self.lensIsLifted = true
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
|
||||
self.liquidLensView?.clipsToBounds = false
|
||||
|
||||
Queue.mainQueue().after(0.1, {
|
||||
self.lensIsLifted = false
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
Queue.mainQueue().after(0.4, {
|
||||
self.liquidLensView?.clipsToBounds = true
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
self.selectionGestureState = nil
|
||||
if case .ended = recognizer.state {
|
||||
guard let index = self.buttons.firstIndex(where: { AnyHashable($0.key) == selectionGestureState.itemId }) else {
|
||||
return
|
||||
}
|
||||
let button = self.buttons[index]
|
||||
self.skipLensUpdate = true
|
||||
if self.selectionChanged(button) {
|
||||
self.selectedIndex = index
|
||||
if self.buttons.count > 5, let button = self.itemViews[button.key] {
|
||||
let transition = ComponentTransition.spring(duration: 0.4)
|
||||
|
||||
let scrollView = self.scrollNode.view
|
||||
let targetRect = button.frame.insetBy(dx: -35.0, dy: 0.0)
|
||||
|
||||
var newBounds = scrollView.bounds
|
||||
if targetRect.minX < scrollView.bounds.minX {
|
||||
newBounds.origin.x = targetRect.minX
|
||||
}
|
||||
else if targetRect.maxX > scrollView.bounds.maxX {
|
||||
newBounds.origin.x = targetRect.maxX - scrollView.bounds.width
|
||||
}
|
||||
let minX = 0.0
|
||||
let maxX = scrollView.contentSize.width - scrollView.bounds.width
|
||||
newBounds.origin.x = max(minX, min(newBounds.origin.x, maxX))
|
||||
|
||||
transition.setBounds(view: scrollView, bounds: newBounds)
|
||||
self.updateItemContainers(contentOffset: newBounds.minX, transition: transition)
|
||||
}
|
||||
}
|
||||
self.skipLensUpdate = false
|
||||
}
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func updateViews(transition: ComponentTransition) {
|
||||
guard let layout = self.validLayout else {
|
||||
return
|
||||
@@ -1486,18 +1602,16 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
case .legacy:
|
||||
panelSideInset = 3.0
|
||||
}
|
||||
|
||||
let visibleRect = self.scrollNode.bounds.insetBy(dx: -180.0, dy: 0.0)
|
||||
|
||||
|
||||
var distanceBetweenNodes = floorToScreenPixels((width - panelSideInset * 2.0 - self.buttonSize.width) / CGFloat(max(1, self.buttons.count - 1)))
|
||||
let internalWidth = distanceBetweenNodes * CGFloat(self.buttons.count - 1)
|
||||
var buttonWidth = self.buttonSize.width
|
||||
var leftNodeOriginX: CGFloat
|
||||
var maxButtonsToFit = 6
|
||||
var maxButtonsToFit = 5
|
||||
switch self.panelStyle {
|
||||
case .glass:
|
||||
leftNodeOriginX = layout.safeInsets.left + 3.0 + buttonWidth / 2.0
|
||||
if layout.size.width < 400.0 {
|
||||
leftNodeOriginX = layout.safeInsets.left + buttonWidth / 2.0
|
||||
if layout.size.width < 420.0 {
|
||||
maxButtonsToFit = 5
|
||||
}
|
||||
case .legacy:
|
||||
@@ -1513,7 +1627,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
buttonWidth = smallButtonWidth
|
||||
distanceBetweenNodes = 60.0
|
||||
}
|
||||
leftNodeOriginX = layout.safeInsets.left + 3.0 + buttonWidth / 2.0
|
||||
leftNodeOriginX = layout.safeInsets.left + buttonWidth / 2.0
|
||||
}
|
||||
|
||||
var validIds = Set<AnyHashable>()
|
||||
@@ -1521,25 +1635,28 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
var selectionFrame = CGRect()
|
||||
var mostRightX = 0.0
|
||||
for i in 0 ..< self.buttons.count {
|
||||
let originX = floor(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: originX, y: 0.0), size: CGSize(width: buttonWidth, height: self.buttonSize.height))
|
||||
let originX = floorToScreenPixels(leftNodeOriginX + CGFloat(i) * distanceBetweenNodes - buttonWidth / 2.0)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: originX, y: -3.0), size: CGSize(width: buttonWidth, height: self.buttonSize.height))
|
||||
mostRightX = buttonFrame.maxX
|
||||
if !visibleRect.intersects(buttonFrame) {
|
||||
continue
|
||||
}
|
||||
|
||||
let type = self.buttons[i]
|
||||
let _ = validIds.insert(type.key)
|
||||
|
||||
var buttonTransition = transition
|
||||
let buttonView: ComponentHostView<Empty>
|
||||
if let current = self.buttonViews[type.key] {
|
||||
let selectedButtonView: ComponentHostView<Empty>
|
||||
if let current = self.itemViews[type.key], let currentSelected = self.selectedItemViews[type.key] {
|
||||
buttonView = current
|
||||
selectedButtonView = currentSelected
|
||||
} else {
|
||||
buttonTransition = .immediate
|
||||
buttonView = ComponentHostView<Empty>()
|
||||
self.buttonViews[type.key] = buttonView
|
||||
self.scrollNode.view.addSubview(buttonView)
|
||||
self.itemViews[type.key] = buttonView
|
||||
self.itemsContainer.addSubview(buttonView)
|
||||
|
||||
selectedButtonView = ComponentHostView<Empty>()
|
||||
self.selectedItemViews[type.key] = selectedButtonView
|
||||
self.selectedItemsContainer.addSubview(selectedButtonView)
|
||||
}
|
||||
|
||||
if case let .app(bot) = type {
|
||||
@@ -1578,7 +1695,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
style: self.panelStyle == .glass ? .glass : .legacy,
|
||||
type: type,
|
||||
isFirstOrLast: i == 0 || i == self.buttons.count - 1,
|
||||
isSelected: i == self.selectedIndex,
|
||||
isSelected: false,
|
||||
strings: self.presentationData.strings,
|
||||
theme: self.presentationData.theme,
|
||||
action: { [weak self] in
|
||||
@@ -1587,7 +1704,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
strongSelf.selectedIndex = i
|
||||
strongSelf.updateViews(transition: .init(animation: .curve(duration: 0.2, curve: .spring)))
|
||||
|
||||
if strongSelf.buttons.count > 5, let button = strongSelf.buttonViews[type.key] {
|
||||
if strongSelf.buttons.count > 5, let button = strongSelf.itemViews[type.key] {
|
||||
strongSelf.scrollNode.view.scrollRectToVisible(button.frame.insetBy(dx: -35.0, dy: 0.0), animated: true)
|
||||
}
|
||||
}
|
||||
@@ -1601,11 +1718,32 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: buttonWidth, height: self.buttonSize.height)
|
||||
)
|
||||
self.itemSizes[type.key] = actualButtonSize
|
||||
|
||||
let _ = selectedButtonView.update(
|
||||
transition: buttonTransition,
|
||||
component: AnyComponent(AttachButtonComponent(
|
||||
context: self.context,
|
||||
style: self.panelStyle == .glass ? .glass : .legacy,
|
||||
type: type,
|
||||
isFirstOrLast: i == 0 || i == self.buttons.count - 1,
|
||||
isSelected: true,
|
||||
strings: self.presentationData.strings,
|
||||
theme: self.presentationData.theme,
|
||||
action: {
|
||||
},
|
||||
longPressAction: {
|
||||
})
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: buttonWidth, height: self.buttonSize.height)
|
||||
)
|
||||
|
||||
if i == self.selectedIndex {
|
||||
selectionFrame = CGRect(origin: CGPoint(x: buttonFrame.midX - actualButtonSize.width * 0.5, y: buttonFrame.minY), size: actualButtonSize)
|
||||
}
|
||||
buttonTransition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
buttonTransition.setFrame(view: selectedButtonView, frame: buttonFrame)
|
||||
var accessibilityTitle = ""
|
||||
switch type {
|
||||
case .gallery:
|
||||
@@ -1634,14 +1772,14 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
buttonView.accessibilityTraits = [.button]
|
||||
}
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, itemView) in self.buttonViews {
|
||||
for (id, itemView) in self.itemViews {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.buttonViews.removeValue(forKey: id)
|
||||
self.itemViews.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
selectionFrame = selectionFrame.insetBy(dx: 1.0, dy: 4.0)
|
||||
@@ -1651,9 +1789,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
mostRightX += layout.safeInsets.right + 3.0
|
||||
|
||||
let contentSize = CGSize(width: mostRightX, height: self.buttonSize.height)
|
||||
if contentSize != self.scrollNode.view.contentSize {
|
||||
if contentSize != self.scrollNode.view.contentSize && self.scrollNode.view.bounds.width > 0.0 {
|
||||
self.scrollNode.view.contentSize = contentSize
|
||||
self.scrollNode.view.isScrollEnabled = abs(contentSize.width - self.scrollNode.view.bounds.width) > 1.0
|
||||
self.scrollNode.view.isScrollEnabled = contentSize.width - self.scrollNode.view.bounds.width > 1.0
|
||||
self.liquidLensView?.clipsToBounds = self.scrollNode.view.isScrollEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1916,31 +2055,65 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
textPanelWidth = layout.size.width - panelSideInset * 2.0
|
||||
}
|
||||
|
||||
self.updateViews(transition: .immediate)
|
||||
|
||||
let glassPanelHeight: CGFloat = 62.0
|
||||
let bounds = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: self.buttonSize.height + insets.bottom))
|
||||
if case .glass = self.panelStyle {
|
||||
let backgroundView: GlassBackgroundView
|
||||
if let current = self.backgroundView {
|
||||
backgroundView = current
|
||||
let liquidLensView: LiquidLensView
|
||||
if let currentBackground = self.backgroundView, let currentLens = self.liquidLensView {
|
||||
backgroundView = currentBackground
|
||||
liquidLensView = currentLens
|
||||
} else {
|
||||
backgroundView = GlassBackgroundView()
|
||||
self.containerNode.view.addSubview(backgroundView)
|
||||
self.containerNode.view.addSubview(self.scrollNode.view)
|
||||
self.backgroundView = backgroundView
|
||||
|
||||
liquidLensView = LiquidLensView(kind: .noContainer)
|
||||
liquidLensView.clipsToBounds = true
|
||||
liquidLensView.layer.cornerRadius = 28.0
|
||||
|
||||
self.containerNode.view.addSubview(backgroundView)
|
||||
self.containerNode.view.addSubview(liquidLensView)
|
||||
self.containerNode.view.addSubview(self.scrollNode.view)
|
||||
self.liquidLensView = liquidLensView
|
||||
|
||||
liquidLensView.contentView.addSubview(self.itemsContainer)
|
||||
liquidLensView.selectedContentView.addSubview(self.selectedItemsContainer)
|
||||
|
||||
self.itemsContainer.clipsToBounds = true
|
||||
self.itemsContainer.layer.cornerRadius = 28.0
|
||||
self.selectedItemsContainer.clipsToBounds = true
|
||||
self.selectedItemsContainer.layer.cornerRadius = 28.0
|
||||
|
||||
let tabSelectionRecognizer = TabSelectionRecognizer(target: self, action: #selector(self.onTabSelectionGesture(_:)))
|
||||
tabSelectionRecognizer.delegate = self.wrappedGestureRecognizerDelegate
|
||||
self.tabSelectionRecognizer = tabSelectionRecognizer
|
||||
self.scrollNode.view.addGestureRecognizer(tabSelectionRecognizer)
|
||||
}
|
||||
let panelSize = CGSize(width: isSelecting ? textPanelWidth : layout.size.width - layout.safeInsets.left - layout.safeInsets.right - panelSideInset * 2.0, height: isSelecting ? textPanelHeight - 11.0 : glassPanelHeight)
|
||||
|
||||
let backgroundViewColor: UIColor
|
||||
if self.presentationData.theme.overallDarkAppearance {
|
||||
backgroundViewColor = self.presentationData.theme.list.modalBlocksBackgroundColor.withAlphaComponent(0.55)
|
||||
} else {
|
||||
backgroundViewColor = self.presentationData.theme.list.plainBackgroundColor.withAlphaComponent(0.75)
|
||||
}
|
||||
|
||||
let cornerRadius: CGFloat = isSelecting ? min(20.0, panelSize.height * 0.5) : panelSize.height * 0.5
|
||||
let backgroundOriginX: CGFloat = isSelecting ? panelSideInset : floorToScreenPixels((layout.size.width - panelSize.width) / 2.0)
|
||||
backgroundView.update(size: panelSize, cornerRadius: isSelecting ? 20.0 : glassPanelHeight * 0.5, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .custom, color: backgroundViewColor), transition: ComponentTransition(transition))
|
||||
|
||||
backgroundView.update(size: panelSize, cornerRadius: cornerRadius, isDark: self.presentationData.theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: ComponentTransition(transition))
|
||||
|
||||
let lensSideInset: CGFloat = defaultPanelSideInset + layout.safeInsets.left
|
||||
let lensPanelSize = CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - lensSideInset * 2.0, height: glassPanelHeight)
|
||||
self.lensParams = (lensPanelSize, cornerRadius)
|
||||
self.updateLiquidLens(transition: ComponentTransition(transition))
|
||||
|
||||
transition.updatePosition(layer: liquidLensView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5))
|
||||
transition.updateBounds(layer: liquidLensView.layer, bounds: CGRect(origin: .zero, size: CGSize(width: lensPanelSize.width - 3.0 * 2.0, height: lensPanelSize.height - 3.0 * 2.0)))
|
||||
|
||||
transition.updatePosition(layer: backgroundView.layer, position: CGPoint(x: backgroundOriginX + panelSize.width * 0.5, y: panelSize.height * 0.5))
|
||||
transition.updateBounds(layer: backgroundView.layer, bounds: CGRect(origin: .zero, size: panelSize))
|
||||
|
||||
let itemsContainerFrame = CGRect(origin: .zero, size: CGSize(width: lensPanelSize.width - 3.0 * 2.0, height: lensPanelSize.height - 3.0 * 2.0))
|
||||
transition.updateBounds(layer: self.itemsContainer.layer, bounds: CGRect(origin: .zero, size: itemsContainerFrame.size))
|
||||
transition.updateBounds(layer: self.selectedItemsContainer.layer, bounds: CGRect(origin: .zero, size: itemsContainerFrame.size))
|
||||
transition.updatePosition(layer: self.itemsContainer.layer, position: itemsContainerFrame.center)
|
||||
transition.updatePosition(layer: self.selectedItemsContainer.layer, position: itemsContainerFrame.center)
|
||||
}
|
||||
|
||||
var containerTransition: ContainedViewLayoutTransition
|
||||
@@ -2002,8 +2175,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
let alphaTransition = ContainedViewLayoutTransition.animated(duration: isSelecting ? 0.1 : 0.25, curve: .easeInOut)
|
||||
alphaTransition.updateAlpha(node: self.scrollNode, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0)
|
||||
containerTransition.updateTransformScale(node: self.scrollNode, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0)
|
||||
if let backgroundView = self.backgroundView {
|
||||
containerTransition.updateTransformScale(layer: backgroundView.layer, scale: isAnyButtonVisible ? 0.85 : 1.0)
|
||||
|
||||
if let liquidLensView = self.liquidLensView {
|
||||
alphaTransition.updateAlpha(layer: liquidLensView.layer, alpha: isSelecting || isAnyButtonVisible ? 0.0 : 1.0)
|
||||
containerTransition.updateTransformScale(layer: liquidLensView.layer, scale: isSelecting || isAnyButtonVisible ? 0.85 : 1.0)
|
||||
}
|
||||
|
||||
if isSelectingUpdated {
|
||||
@@ -2022,7 +2197,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 1.0)
|
||||
|
||||
let componentTransition = ComponentTransition.easeInOut(duration: 0.25)
|
||||
componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 0.0, toRadius: 10.0)
|
||||
if let liquidLensView = self.liquidLensView {
|
||||
componentTransition.animateBlur(layer: liquidLensView.layer, fromRadius: 0.0, toRadius: 10.0)
|
||||
transition.animatePositionAdditive(layer: liquidLensView.layer, offset: .zero, to: CGPoint(x: -27.0, y: 0.0))
|
||||
}
|
||||
|
||||
let blurTransition = ComponentTransition.easeInOut(duration: 0.18)
|
||||
transition.animateTransformScale(node: textInputPanelNode.opaqueActionButtons, from: 0.01)
|
||||
@@ -2030,9 +2208,12 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
textInputPanelNode.opaqueActionButtons.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, from: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta))
|
||||
|
||||
blurTransition.animateBlur(layer: textInputPanelNode.inputModeView.layer, fromRadius: 4.0, toRadius: 0.0)
|
||||
componentTransition.animateAlpha(view: textInputPanelNode.inputModeView, from: 0.0, to: 1.0)
|
||||
|
||||
transition.animatePositionAdditive(layer: textInputPanelNode.textPlaceholderNode.layer, offset: CGPoint(x: 6.0, y: heightDelta))
|
||||
transition.animatePositionAdditive(layer: textInputPanelNode.inputModeView.layer, offset: CGPoint(x: 64.0, y: heightDelta))
|
||||
|
||||
|
||||
textInputPanelNode.animateIn(transition: transition)
|
||||
} else {
|
||||
textInputPanelNode.alpha = 1.0
|
||||
@@ -2054,7 +2235,10 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
alphaTransition.updateAlpha(node: textInputPanelNode, alpha: 0.0)
|
||||
|
||||
let componentTransition = ComponentTransition.easeInOut(duration: 0.25)
|
||||
componentTransition.animateBlur(layer: self.scrollNode.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
if let liquidLensView = self.liquidLensView {
|
||||
componentTransition.animateBlur(layer: liquidLensView.layer, fromRadius: 10.0, toRadius: 0.0)
|
||||
transition.animatePositionAdditive(layer: liquidLensView.layer, offset: CGPoint(x: -27.0, y: 0.0))
|
||||
}
|
||||
|
||||
let blurTransition = ComponentTransition.easeInOut(duration: 0.18)
|
||||
transition.animateTransformScale(layer: textInputPanelNode.opaqueActionButtons.layer, from: CGPoint(x: 1.0, y: 1.0), to: CGPoint(x: 0.01, y: 0.01))
|
||||
@@ -2062,6 +2246,9 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
textInputPanelNode.opaqueActionButtons.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2)
|
||||
transition.animatePosition(node: textInputPanelNode.opaqueActionButtons, to: CGPoint(x: textInputPanelNode.opaqueActionButtons.position.x, y: textInputPanelNode.opaqueActionButtons.position.y + heightDelta))
|
||||
|
||||
blurTransition.animateBlur(layer: textInputPanelNode.inputModeView.layer, fromRadius: 0.0, toRadius: 4.0)
|
||||
componentTransition.animateAlpha(view: textInputPanelNode.inputModeView, from: 1.0, to: 0.0)
|
||||
|
||||
transition.animatePositionAdditive(layer: textInputPanelNode.textPlaceholderNode.layer, offset: .zero, to: CGPoint(x: 6.0, y: heightDelta))
|
||||
transition.animatePositionAdditive(layer: textInputPanelNode.inputModeView.layer, offset: .zero, to: CGPoint(x: 64.0, y: heightDelta))
|
||||
} else {
|
||||
@@ -2086,8 +2273,6 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
transition.updateFrameAsPositionAndBounds(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: self.isSelecting ? panelSideInset - defaultPanelSideInset : panelSideInset, y: self.isSelecting ? -11.0 : 0.0), size: CGSize(width: layout.size.width - panelSideInset * 2.0, height: self.buttonSize.height)))
|
||||
}
|
||||
|
||||
self.updateViews(transition: .immediate)
|
||||
|
||||
if let progress = self.loadingProgress {
|
||||
let loadingProgressNode: LoadingProgressNode
|
||||
if let current = self.progressNode {
|
||||
@@ -2183,8 +2368,70 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate {
|
||||
return containerFrame.height
|
||||
}
|
||||
|
||||
func updateItemContainers(contentOffset: CGFloat, transition: ComponentTransition) {
|
||||
let transform = CATransform3DMakeTranslation(-contentOffset, 0.0, 0.0)
|
||||
transition.setSublayerTransform(view: self.itemsContainer, transform: transform)
|
||||
transition.setSublayerTransform(view: self.selectedItemsContainer, transform: transform)
|
||||
}
|
||||
|
||||
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
|
||||
if self.selectionGestureState != nil {
|
||||
self.selectionGestureState = nil
|
||||
self.requestLayout(transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
self.updateItemContainers(contentOffset: scrollView.contentOffset.x, transition: .immediate)
|
||||
self.updateViews(transition: .immediate)
|
||||
self.updateLiquidLens(transition: .immediate)
|
||||
}
|
||||
|
||||
private var skipLensUpdate = false
|
||||
private var lensParams: (panelSize: CGSize, cornerRadius: CGFloat)?
|
||||
func updateLiquidLens(transition: ComponentTransition) {
|
||||
guard !self.skipLensUpdate, let liquidLensView = self.liquidLensView, let (panelSize, _) = self.lensParams else {
|
||||
return
|
||||
}
|
||||
|
||||
var selectionFrame = CGRect()
|
||||
if self.selectedIndex >= 0 && self.selectedIndex < self.buttons.count, let itemView = self.itemViews[self.buttons[self.selectedIndex].key], let itemSize = self.itemSizes[self.buttons[self.selectedIndex].key] {
|
||||
let contentOffset = self.scrollNode.view.contentOffset.x
|
||||
selectionFrame = CGRect(origin: CGPoint(x: itemView.center.x - itemSize.width / 2.0 - contentOffset, y: itemView.frame.minY), size: itemSize)
|
||||
}
|
||||
|
||||
var lensSelection: (x: CGFloat, width: CGFloat)
|
||||
if let selectionGestureState = self.selectionGestureState, selectionGestureState.isLifted {
|
||||
lensSelection = (selectionGestureState.currentX, selectionFrame.width)
|
||||
} else {
|
||||
lensSelection = (selectionFrame.minX, selectionFrame.width)
|
||||
}
|
||||
|
||||
if !self.scrollNode.view.isScrollEnabled {
|
||||
lensSelection.x = max(0.0, min(lensSelection.x, panelSize.width - lensSelection.width))
|
||||
}
|
||||
|
||||
var isLifted = self.selectionGestureState?.isLifted == true || self.lensIsLifted
|
||||
if let widthClass = self.validLayout?.metrics.widthClass, case .regular = widthClass {
|
||||
isLifted = false
|
||||
}
|
||||
|
||||
let inset: CGFloat = 3.0
|
||||
liquidLensView.update(size: CGSize(width: panelSize.width - inset * 2.0, height: panelSize.height - inset * 2.0), cornerRadius: 28.0, selectionOrigin: CGPoint(x: lensSelection.x, y: 0.0), selectionSize: CGSize(width: lensSelection.width, height: panelSize.height - inset * 2.0), inset: 0.0, isDark: self.presentationData.theme.overallDarkAppearance, isLifted: isLifted, isCollapsed: self.isSelecting || self.buttons.count < 2, transition: transition)
|
||||
}
|
||||
|
||||
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer == self.tabSelectionRecognizer {
|
||||
return true
|
||||
}
|
||||
return super.gestureRecognizerShouldBegin(gestureRecognizer)
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/Premium/PremiumCoinComponent",
|
||||
"//submodules/TelegramUI/Components/PlainButtonComponent",
|
||||
"//submodules/Utils/DeviceModel",
|
||||
"//submodules/PresentationDataUtils",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -5,7 +5,9 @@ import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ProgressNavigationButtonNode
|
||||
import AccountContext
|
||||
|
||||
public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
private var controllerNode: AuthorizationSequenceCodeEntryControllerNode {
|
||||
@@ -14,6 +16,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let strings: PresentationStrings
|
||||
private let theme: PresentationTheme
|
||||
|
||||
@@ -45,7 +48,8 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
return self.data?.6 ?? false
|
||||
}
|
||||
|
||||
public init(presentationData: PresentationData, back: @escaping () -> Void) {
|
||||
public init(sharedContext: SharedAccountContext, presentationData: PresentationData, back: @escaping () -> Void) {
|
||||
self.sharedContext = sharedContext
|
||||
self.strings = presentationData.strings
|
||||
self.theme = presentationData.theme
|
||||
|
||||
@@ -61,11 +65,14 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
return false
|
||||
}
|
||||
self.navigationBar?.backPressed = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
let text: String
|
||||
let proceed: String
|
||||
let stop: String
|
||||
|
||||
if let (_, _, type, _, _, _, _) = self?.data, case .email = type {
|
||||
if let (_, _, type, _, _, _, _) = self.data, case .email = type {
|
||||
text = presentationData.strings.Login_CancelEmailVerification
|
||||
proceed = presentationData.strings.Login_CancelEmailVerificationContinue
|
||||
stop = presentationData.strings.Login_CancelEmailVerificationStop
|
||||
@@ -75,7 +82,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||
stop = presentationData.strings.Login_CancelPhoneVerificationStop
|
||||
}
|
||||
|
||||
self?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: proceed, action: {
|
||||
self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: proceed, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: stop, action: {
|
||||
back()
|
||||
})]), in: .window(.root))
|
||||
|
||||
@@ -9,6 +9,7 @@ import MtProtoKit
|
||||
import MessageUI
|
||||
import CoreTelephony
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
import CountrySelectionUI
|
||||
@@ -33,7 +34,7 @@ private enum InnerState: Equatable {
|
||||
|
||||
public final class AuthorizationSequenceController: NavigationController, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
|
||||
static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme {
|
||||
return NavigationBarTheme(buttonColor: theme.intro.accentTextColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor)
|
||||
return NavigationBarTheme(overallDarkAppearance: theme.overallDarkAppearance, buttonColor: theme.chat.inputPanel.panelControlColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, opaqueBackgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor, edgeEffectColor: .clear, style: .glass)
|
||||
}
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
@@ -247,7 +248,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
let carrier = CTCarrier()
|
||||
let mnc = carrier.mobileNetworkCode ?? "none"
|
||||
|
||||
AuthorizationSequenceController.presentEmailComposeController(address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData)
|
||||
AuthorizationSequenceController.presentEmailComposeController(sharedContext: strongSelf.sharedContext, address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData)
|
||||
}))
|
||||
case .phoneLimitExceeded:
|
||||
text = strongSelf.presentationData.strings.Login_PhoneFloodError
|
||||
@@ -273,7 +274,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
let carrier = CTCarrier()
|
||||
let mnc = carrier.mobileNetworkCode ?? "none"
|
||||
|
||||
AuthorizationSequenceController.presentEmailComposeController(address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData)
|
||||
AuthorizationSequenceController.presentEmailComposeController(sharedContext: strongSelf.sharedContext, address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData)
|
||||
}))
|
||||
case let .generic(info):
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
@@ -295,7 +296,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
errorString = "unknown"
|
||||
}
|
||||
|
||||
AuthorizationSequenceController.presentEmailComposeController(address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData)
|
||||
AuthorizationSequenceController.presentEmailComposeController(sharedContext: strongSelf.sharedContext, address: "recover@telegram.org", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller, presentationData: strongSelf.presentationData)
|
||||
}))
|
||||
case .timeout:
|
||||
text = strongSelf.presentationData.strings.Login_NetworkError
|
||||
@@ -307,7 +308,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
controller.present(strongSelf.sharedContext.makeProxySettingsController(sharedContext: strongSelf.sharedContext, account: strongSelf.account), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}))
|
||||
}
|
||||
(controller.navigationController as? NavigationController)?.presentOverlay(controller: standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), inGlobal: true, blockInteraction: true)
|
||||
(controller.navigationController as? NavigationController)?.presentOverlay(controller: textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: actions), inGlobal: true, blockInteraction: true)
|
||||
|
||||
controller.dismissConfirmation()
|
||||
}
|
||||
@@ -352,7 +353,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}))
|
||||
@@ -376,7 +377,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
if let currentController = currentController {
|
||||
controller = currentController
|
||||
} else {
|
||||
controller = AuthorizationSequenceCodeEntryController(presentationData: self.presentationData, back: { [weak self] in
|
||||
controller = AuthorizationSequenceCodeEntryController(sharedContext: self.sharedContext, presentationData: self.presentationData, back: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@@ -451,7 +452,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
let _ = self.engine.auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).startStandalone()
|
||||
}
|
||||
|
||||
controller.presentInGlobalOverlay(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]))
|
||||
controller.presentInGlobalOverlay(textAlertController(sharedContext: self.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]))
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -510,7 +511,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
controller.resetCode()
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -623,7 +624,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
controller.resetCode()
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -645,7 +646,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
let mnc = carrier.mobileNetworkCode ?? "none"
|
||||
let _ = strongSelf.engine.auth.reportMissingCode(phoneNumber: number, phoneCodeHash: phoneCodeHash, mnc: mnc).start()
|
||||
|
||||
AuthorizationSequenceController.presentDidNotGetCodeUI(controller: controller, presentationData: strongSelf.presentationData, phoneNumber: number, mnc: mnc)
|
||||
AuthorizationSequenceController.presentDidNotGetCodeUI(sharedContext: strongSelf.sharedContext, controller: controller, presentationData: strongSelf.presentationData, phoneNumber: number, mnc: mnc)
|
||||
}
|
||||
} else {
|
||||
controller?.inProgress = true
|
||||
@@ -681,7 +682,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
text = strongSelf.presentationData.strings.Login_NetworkError
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: actions), in: .window(.root))
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -780,7 +781,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
text = strongSelf.presentationData.strings.Login_EmailNotAllowedError
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}, completed: {
|
||||
controller?.inProgress = false
|
||||
@@ -827,7 +828,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
switch authorization.credential {
|
||||
case let appleIdCredential as ASAuthorizationAppleIDCredential:
|
||||
guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else {
|
||||
lastController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
lastController?.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -850,7 +851,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
case .emailNotAllowed:
|
||||
text = strongSelf.presentationData.strings.Login_EmailNotAllowedError
|
||||
}
|
||||
lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
lastController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
@@ -891,7 +892,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
text = strongSelf.presentationData.strings.Login_InvalidEmailAddressError
|
||||
}
|
||||
|
||||
lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
lastController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -907,7 +908,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
guard let lastController = self.viewControllers.last as? ViewController else {
|
||||
return
|
||||
}
|
||||
lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: error.localizedDescription, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
lastController.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: error.localizedDescription, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
@@ -927,7 +928,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
if let currentController = currentController {
|
||||
controller = currentController
|
||||
} else {
|
||||
controller = AuthorizationSequencePasswordEntryController(presentationData: self.presentationData, back: { [weak self] in
|
||||
controller = AuthorizationSequencePasswordEntryController(sharedContext: self.sharedContext, presentationData: self.presentationData, back: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@@ -954,7 +955,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.passwordIsInvalid()
|
||||
}
|
||||
}
|
||||
@@ -988,14 +989,14 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
|
||||
strongController.inProgress = false
|
||||
|
||||
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
strongController.didForgotWithNoRecovery = true
|
||||
}))
|
||||
}
|
||||
}
|
||||
controller.reset = { [weak self, weak controller] in
|
||||
if let strongSelf = self, let strongController = controller {
|
||||
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: suggestReset ? strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed : strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [
|
||||
strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: suggestReset ? strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed : strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Login_ResetAccountProtected_Reset, action: {
|
||||
if let strongSelf = self, let strongController = controller {
|
||||
@@ -1015,7 +1016,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
case .limitExceeded:
|
||||
text = strongSelf.presentationData.strings.Login_ResetAccountProtected_LimitExceeded
|
||||
}
|
||||
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -1082,7 +1083,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
})
|
||||
controller.reset = { [weak self, weak controller] in
|
||||
if let strongSelf = self, let strongController = controller {
|
||||
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_ResetAccountConfirmation, actions: [
|
||||
strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_ResetAccountConfirmation, actions: [
|
||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Login_ResetAccountProtected_Reset, action: {
|
||||
if let strongSelf = self, let strongController = controller {
|
||||
@@ -1102,7 +1103,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
case .limitExceeded:
|
||||
text = strongSelf.presentationData.strings.Login_ResetAccountProtected_LimitExceeded
|
||||
}
|
||||
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
strongController.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}))
|
||||
}
|
||||
@@ -1132,7 +1133,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
if let currentController = currentController {
|
||||
controller = currentController
|
||||
} else {
|
||||
controller = AuthorizationSequenceSignUpController(presentationData: self.presentationData, back: { [weak self] in
|
||||
controller = AuthorizationSequenceSignUpController(sharedContext: self.sharedContext, presentationData: self.presentationData, back: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@@ -1231,7 +1232,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
}))
|
||||
@@ -1379,7 +1380,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
}
|
||||
}
|
||||
|
||||
static func presentEmailComposeController(address: String, subject: String, body: String, from controller: ViewController, presentationData: PresentationData) {
|
||||
static func presentEmailComposeController(sharedContext: SharedAccountContext, address: String, subject: String, body: String, from controller: ViewController, presentationData: PresentationData) {
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
final class ComposeDelegate: NSObject, MFMailComposeViewControllerDelegate {
|
||||
@objc func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
@@ -1398,7 +1399,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
|
||||
controller.view.window?.rootViewController?.present(composeController, animated: true, completion: nil)
|
||||
} else {
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
controller.present(textAlertController(sharedContext: sharedContext, title: nil, text: presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1451,6 +1452,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
}
|
||||
|
||||
public static func presentDidNotGetCodeUI(
|
||||
sharedContext: SharedAccountContext,
|
||||
controller: ViewController,
|
||||
presentationData: PresentationData,
|
||||
phoneNumber: String,
|
||||
@@ -1470,6 +1472,6 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|
||||
emailBody.append("Locale: \(locale)\n")
|
||||
emailBody.append("MNC: \(mnc)")
|
||||
|
||||
AuthorizationSequenceController.presentEmailComposeController(address: "sms@telegram.org", subject: presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller, presentationData: presentationData)
|
||||
AuthorizationSequenceController.presentEmailComposeController(sharedContext: sharedContext, address: "sms@telegram.org", subject: presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: emailBody, from: controller, presentationData: presentationData)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ProgressNavigationButtonNode
|
||||
import AccountContext
|
||||
|
||||
final class AuthorizationSequencePasswordEntryController: ViewController {
|
||||
private var controllerNode: AuthorizationSequencePasswordEntryControllerNode {
|
||||
@@ -12,6 +14,7 @@ final class AuthorizationSequencePasswordEntryController: ViewController {
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let presentationData: PresentationData
|
||||
|
||||
var loginWithPassword: ((String) -> Void)?
|
||||
@@ -40,7 +43,8 @@ final class AuthorizationSequencePasswordEntryController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
init(presentationData: PresentationData, back: @escaping () -> Void) {
|
||||
init(sharedContext: SharedAccountContext, presentationData: PresentationData, back: @escaping () -> Void) {
|
||||
self.sharedContext = sharedContext
|
||||
self.presentationData = presentationData
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)))
|
||||
@@ -153,10 +157,8 @@ final class AuthorizationSequencePasswordEntryController: ViewController {
|
||||
}
|
||||
|
||||
func forgotPressed() {
|
||||
/*if self.suggestReset {
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
} else*/ if self.didForgotWithNoRecovery {
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
if self.didForgotWithNoRecovery {
|
||||
self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
} else {
|
||||
self.forgot?()
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT
|
||||
self.codeField.textField.tintColor = self.theme.list.itemAccentColor
|
||||
self.codeField.textField.accessibilityHint = self.strings.Login_VoiceOver_Password
|
||||
|
||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0)
|
||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), glass: true, height: 50.0, cornerRadius: 50.0 * 0.5)
|
||||
self.proceedNode.progressType = .embedded
|
||||
self.proceedNode.isEnabled = false
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ final class AuthorizationSequencePaymentScreenComponent: Component {
|
||||
).string
|
||||
|
||||
let presentationData = component.presentationData
|
||||
AuthorizationSequenceController.presentEmailComposeController(address: component.supportEmailAddress, subject: environment.strings.Login_PhonePaidEmailSubject, body: body, from: controller, presentationData: presentationData)
|
||||
AuthorizationSequenceController.presentEmailComposeController(sharedContext: component.sharedContext, address: component.supportEmailAddress, subject: environment.strings.Login_PhonePaidEmailSubject, body: body, from: controller, presentationData: presentationData)
|
||||
}
|
||||
|
||||
func update(component: AuthorizationSequencePaymentScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
|
||||
@@ -6,6 +6,7 @@ import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import Postbox
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import ProgressNavigationButtonNode
|
||||
import AccountContext
|
||||
import CountrySelectionUI
|
||||
@@ -91,7 +92,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
|
||||
}
|
||||
|
||||
if !otherAccountPhoneNumbers.1.isEmpty {
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "___close", style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
}
|
||||
|
||||
if let countriesConfiguration {
|
||||
@@ -173,7 +174,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
|
||||
|
||||
self.controllerNode.selectCountryCode = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.presentationData.strings, theme: strongSelf.presentationData.theme)
|
||||
let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.presentationData.strings, theme: strongSelf.presentationData.theme, glass: true)
|
||||
controller.completeWithCountryCode = { code, name in
|
||||
if let strongSelf = self, let currentData = strongSelf.currentData {
|
||||
strongSelf.updateData(countryCode: Int32(code), countryName: name, number: currentData.2)
|
||||
@@ -404,7 +405,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
|
||||
}))
|
||||
}
|
||||
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}))
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root))
|
||||
self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root))
|
||||
} else {
|
||||
if let validLayout = self.validLayout, validLayout.size.width > 320.0 {
|
||||
let (code, formattedNumber) = self.controllerNode.formattedCodeAndNumber
|
||||
@@ -425,7 +426,7 @@ public final class AuthorizationSequencePhoneEntryController: ViewController, MF
|
||||
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts)
|
||||
}
|
||||
}))
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: logInNumber, text: self.presentationData.strings.Login_PhoneNumberConfirmation, actions: actions), in: .window(.root))
|
||||
self.present(textAlertController(sharedContext: self.sharedContext, title: logInNumber, text: self.presentationData.strings.Login_PhoneNumberConfirmation, actions: actions), in: .window(.root))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -423,7 +423,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
|
||||
self.phoneAndCountryNode = PhoneAndCountryNode(strings: strings, theme: theme)
|
||||
|
||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0)
|
||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), glass: true, height: 50.0, cornerRadius: 50 * 0.5)
|
||||
self.proceedNode.progressType = .embedded
|
||||
self.proceedNode.isEnabled = false
|
||||
|
||||
@@ -857,7 +857,7 @@ final class PhoneConfirmationController: ViewController {
|
||||
self.cancelButton.accessibilityTraits = [.button]
|
||||
self.cancelButton.accessibilityLabel = strings.Login_Edit
|
||||
|
||||
self.proceedNode = SolidRoundedButtonNode(title: strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0)
|
||||
self.proceedNode = SolidRoundedButtonNode(title: strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: theme), glass: true, height: 50.0, cornerRadius: 50.0 * 0.5)
|
||||
self.proceedNode.progressType = .embedded
|
||||
|
||||
let font = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
|
||||
|
||||
@@ -5,6 +5,7 @@ import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import LegacyComponents
|
||||
import ProgressNavigationButtonNode
|
||||
import ImageCompression
|
||||
@@ -13,6 +14,7 @@ import Postbox
|
||||
import TextFormat
|
||||
import MoreButtonNode
|
||||
import ContextUI
|
||||
import AccountContext
|
||||
|
||||
final class AuthorizationSequenceSignUpController: ViewController {
|
||||
private var controllerNode: AuthorizationSequenceSignUpControllerNode {
|
||||
@@ -23,6 +25,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
|
||||
private let moreButtonNode: MoreButtonNode
|
||||
|
||||
private let sharedContext: SharedAccountContext
|
||||
private let presentationData: PresentationData
|
||||
private let back: () -> Void
|
||||
|
||||
@@ -46,7 +49,8 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
init(presentationData: PresentationData, back: @escaping () -> Void, displayCancel: Bool) {
|
||||
init(sharedContext: SharedAccountContext, presentationData: PresentationData, back: @escaping () -> Void, displayCancel: Bool) {
|
||||
self.sharedContext = sharedContext
|
||||
self.presentationData = presentationData
|
||||
self.back = back
|
||||
|
||||
@@ -68,7 +72,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Login_CancelPhoneVerificationContinue, action: {
|
||||
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Login_CancelPhoneVerificationContinue, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_CancelPhoneVerificationStop, action: {
|
||||
back()
|
||||
})]), in: .window(.root))
|
||||
@@ -92,7 +96,7 @@ final class AuthorizationSequenceSignUpController: ViewController {
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Login_CancelPhoneVerificationContinue, action: {
|
||||
self.present(textAlertController(sharedContext: self.sharedContext, title: nil, text: self.presentationData.strings.Login_CancelSignUpConfirmation, actions: [TextAlertAction(type: .genericAction, title: self.presentationData.strings.Login_CancelPhoneVerificationContinue, action: {
|
||||
}), TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Login_CancelPhoneVerificationStop, action: { [weak self] in
|
||||
self?.back()
|
||||
})]), in: .window(.root))
|
||||
|
||||
@@ -73,10 +73,12 @@ public final class AuthorizationSequenceSplashController: ViewController {
|
||||
|
||||
self.controller = RMIntroViewController(backgroundColor: theme.list.plainBackgroundColor, primaryColor: theme.list.itemPrimaryTextColor, buttonColor: theme.intro.startButtonColor, accentColor: theme.list.itemAccentColor, regularDotColor: theme.intro.dotColor, highlightedDotColor: theme.list.itemAccentColor, suggestedLocalizationSignal: localizationSignal)
|
||||
|
||||
self.startButton = SolidRoundedButtonNode(title: "Start Messaging", theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 13.0, isShimmering: true)
|
||||
self.startButton = SolidRoundedButtonNode(title: "Start Messaging", theme: SolidRoundedButtonTheme(theme: theme), glass: true, height: 50.0, cornerRadius: 50.0 * 0.5, isShimmering: true)
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
self._hasGlassStyle = true
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
|
||||
|
||||
@@ -114,7 +114,7 @@ class BotCheckoutHeaderItemNode: ListViewItemNode {
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.isLayerBacked = true
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.imageNode)
|
||||
|
||||
@@ -113,7 +113,7 @@ class BotCheckoutPriceItemNode: ListViewItemNode {
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
self.addSubnode(self.titleNode)
|
||||
|
||||
@@ -509,7 +509,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate {
|
||||
self.maskNode = ASImageNode()
|
||||
self.maskNode.isUserInteractionEnabled = false
|
||||
|
||||
super.init(layerBacked: false, dynamicBounce: false)
|
||||
super.init(layerBacked: false)
|
||||
|
||||
self.addSubnode(self.backgroundNode)
|
||||
|
||||
|
||||
@@ -50,6 +50,13 @@ swift_library(
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/Utils/DeviceModel",
|
||||
"//submodules/LegacyMediaPickerUI",
|
||||
"//submodules/TelegramUI/Components/AlertComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBackgroundComponent",
|
||||
"//submodules/TelegramUI/Components/EdgeEffect",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
"//submodules/TelegramUI/Components/SearchInputPanelComponent",
|
||||
"//submodules/TelegramUI/Components/GlassControls",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@@ -9,6 +9,9 @@ import AccountContext
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
import UrlEscaping
|
||||
import GlassBackgroundComponent
|
||||
import GlassBarButtonComponent
|
||||
import EdgeEffect
|
||||
|
||||
final class AddressBarContentComponent: Component {
|
||||
public typealias EnvironmentType = BrowserNavigationBarEnvironment
|
||||
@@ -19,6 +22,8 @@ final class AddressBarContentComponent: Component {
|
||||
let url: String
|
||||
let isSecure: Bool
|
||||
let isExpanded: Bool
|
||||
let readingProgress: CGFloat
|
||||
let loadingProgress: Double?
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
|
||||
init(
|
||||
@@ -28,6 +33,8 @@ final class AddressBarContentComponent: Component {
|
||||
url: String,
|
||||
isSecure: Bool,
|
||||
isExpanded: Bool,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?,
|
||||
performAction: ActionSlot<BrowserScreen.Action>
|
||||
) {
|
||||
self.theme = theme
|
||||
@@ -36,6 +43,8 @@ final class AddressBarContentComponent: Component {
|
||||
self.url = url
|
||||
self.isSecure = isSecure
|
||||
self.isExpanded = isExpanded
|
||||
self.readingProgress = readingProgress
|
||||
self.loadingProgress = loadingProgress
|
||||
self.performAction = performAction
|
||||
}
|
||||
|
||||
@@ -58,6 +67,12 @@ final class AddressBarContentComponent: Component {
|
||||
if lhs.isExpanded != rhs.isExpanded {
|
||||
return false
|
||||
}
|
||||
if lhs.readingProgress != rhs.readingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.loadingProgress != rhs.loadingProgress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -85,6 +100,8 @@ final class AddressBarContentComponent: Component {
|
||||
var isSecure: Bool
|
||||
var collapseFraction: CGFloat
|
||||
var isTablet: Bool
|
||||
var readingProgress: CGFloat
|
||||
var loadingProgress: Double?
|
||||
|
||||
static func ==(lhs: Params, rhs: Params) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
@@ -111,26 +128,33 @@ final class AddressBarContentComponent: Component {
|
||||
if lhs.isTablet != rhs.isTablet {
|
||||
return false
|
||||
}
|
||||
if lhs.readingProgress != rhs.readingProgress {
|
||||
return false
|
||||
}
|
||||
if lhs.loadingProgress != rhs.loadingProgress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private let activated: (Bool) -> Void = { _ in }
|
||||
private let deactivated: (Bool) -> Void = { _ in }
|
||||
|
||||
private let backgroundLayer: SimpleLayer
|
||||
|
||||
private let iconView: UIImageView
|
||||
|
||||
private let backgroundView: GlassBackgroundView
|
||||
|
||||
private let clearIconView: UIImageView
|
||||
private let clearIconButton: HighlightTrackingButton
|
||||
|
||||
private let cancelButtonTitle: ComponentView<Empty>
|
||||
private let cancelButton: HighlightTrackingButton
|
||||
private let cancelButton = ComponentView<Empty>()
|
||||
|
||||
private var placeholderContent = ComponentView<Empty>()
|
||||
private var titleContent = ComponentView<Empty>()
|
||||
|
||||
private let clippingView = UIView()
|
||||
private var loadingProgress = ComponentView<Empty>()
|
||||
private var readingProgressView = UIView()
|
||||
|
||||
private var textFrame: CGRect?
|
||||
private var textField: TextField?
|
||||
|
||||
@@ -144,50 +168,30 @@ final class AddressBarContentComponent: Component {
|
||||
}
|
||||
|
||||
init() {
|
||||
self.backgroundLayer = SimpleLayer()
|
||||
|
||||
self.iconView = UIImageView()
|
||||
self.backgroundView = GlassBackgroundView()
|
||||
|
||||
self.clearIconView = UIImageView()
|
||||
self.clearIconButton = HighlightableButton()
|
||||
self.clearIconView.isHidden = false
|
||||
self.clearIconButton.isHidden = false
|
||||
|
||||
self.cancelButtonTitle = ComponentView()
|
||||
self.cancelButton = HighlightTrackingButton()
|
||||
|
||||
|
||||
super.init(frame: CGRect())
|
||||
|
||||
self.layer.addSublayer(self.backgroundLayer)
|
||||
self.clippingView.clipsToBounds = true
|
||||
|
||||
self.addSubview(self.iconView)
|
||||
self.addSubview(self.clearIconView)
|
||||
self.addSubview(self.clearIconButton)
|
||||
self.addSubview(self.backgroundView)
|
||||
self.backgroundView.contentView.addSubview(self.clippingView)
|
||||
self.clippingView.addSubview(self.readingProgressView)
|
||||
|
||||
self.backgroundView.contentView.addSubview(self.clearIconView)
|
||||
self.backgroundView.contentView.addSubview(self.clearIconButton)
|
||||
|
||||
self.addSubview(self.cancelButton)
|
||||
self.clipsToBounds = true
|
||||
|
||||
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||
self.tapRecognizer = tapRecognizer
|
||||
self.addGestureRecognizer(tapRecognizer)
|
||||
|
||||
self.cancelButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view {
|
||||
cancelButtonTitleView.layer.removeAnimation(forKey: "opacity")
|
||||
cancelButtonTitleView.alpha = 0.4
|
||||
}
|
||||
} else {
|
||||
if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view {
|
||||
cancelButtonTitleView.alpha = 1.0
|
||||
cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside)
|
||||
|
||||
|
||||
self.clearIconButton.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
@@ -263,11 +267,16 @@ final class AddressBarContentComponent: Component {
|
||||
self.placeholderContent.view?.isHidden = !text.isEmpty
|
||||
|
||||
if let params = self.params {
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, transition: .immediate)
|
||||
self.update(theme: params.theme, strings: params.strings, size: params.size, isActive: params.isActive, title: params.title, isSecure: params.isSecure, collapseFraction: params.collapseFraction, isTablet: params.isTablet, readingProgress: params.readingProgress, loadingProgress: params.loadingProgress, transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: AddressBarContentComponent, availableSize: CGSize, environment: Environment<BrowserNavigationBarEnvironment>, transition: ComponentTransition) -> CGSize {
|
||||
func update(
|
||||
component: AddressBarContentComponent,
|
||||
availableSize: CGSize,
|
||||
environment: Environment<BrowserNavigationBarEnvironment>,
|
||||
transition: ComponentTransition
|
||||
) -> CGSize {
|
||||
let collapseFraction = environment[BrowserNavigationBarEnvironment.self].fraction
|
||||
|
||||
let wasExpanded = self.component?.isExpanded ?? false
|
||||
@@ -282,12 +291,36 @@ final class AddressBarContentComponent: Component {
|
||||
let isActive = self.textField?.isFirstResponder ?? false
|
||||
|
||||
let title = getDisplayUrl(component.url, hostOnly: true)
|
||||
self.update(theme: component.theme, strings: component.strings, size: availableSize, isActive: isActive, title: title.lowercased(), isSecure: component.isSecure, collapseFraction: collapseFraction, isTablet: component.metrics.isTablet, transition: transition)
|
||||
self.update(
|
||||
theme: component.theme,
|
||||
strings: component.strings,
|
||||
size: availableSize,
|
||||
isActive: isActive,
|
||||
title: title.lowercased(),
|
||||
isSecure: component.isSecure,
|
||||
collapseFraction: collapseFraction,
|
||||
isTablet: component.metrics.isTablet,
|
||||
readingProgress: component.readingProgress,
|
||||
loadingProgress: component.loadingProgress,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
||||
public func update(theme: PresentationTheme, strings: PresentationStrings, size: CGSize, isActive: Bool, title: String, isSecure: Bool, collapseFraction: CGFloat, isTablet: Bool, transition: ComponentTransition) {
|
||||
public func update(
|
||||
theme: PresentationTheme,
|
||||
strings: PresentationStrings,
|
||||
size: CGSize,
|
||||
isActive: Bool,
|
||||
title: String,
|
||||
isSecure: Bool,
|
||||
collapseFraction: CGFloat,
|
||||
isTablet: Bool,
|
||||
readingProgress: CGFloat,
|
||||
loadingProgress: Double?,
|
||||
transition: ComponentTransition
|
||||
) {
|
||||
let params = Params(
|
||||
theme: theme,
|
||||
strings: strings,
|
||||
@@ -296,7 +329,9 @@ final class AddressBarContentComponent: Component {
|
||||
title: title,
|
||||
isSecure: isSecure,
|
||||
collapseFraction: collapseFraction,
|
||||
isTablet: isTablet
|
||||
isTablet: isTablet,
|
||||
readingProgress: readingProgress,
|
||||
loadingProgress: loadingProgress
|
||||
)
|
||||
|
||||
if self.params == params {
|
||||
@@ -306,8 +341,6 @@ final class AddressBarContentComponent: Component {
|
||||
let isActiveWithText = self.component?.isExpanded ?? false
|
||||
|
||||
if self.params?.theme !== theme {
|
||||
self.iconView.image = generateTintedImage(image: UIImage(bundleImageName: "Media Grid/Lock"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.iconView.tintColor = theme.rootController.navigationSearchBar.inputIconColor
|
||||
self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate)
|
||||
self.clearIconView.tintColor = theme.rootController.navigationSearchBar.inputClearButtonColor
|
||||
}
|
||||
@@ -315,57 +348,9 @@ final class AddressBarContentComponent: Component {
|
||||
self.params = params
|
||||
|
||||
let sideInset: CGFloat = 10.0
|
||||
let inputHeight: CGFloat = 36.0
|
||||
let inputHeight: CGFloat = 44.0
|
||||
let topInset: CGFloat = (size.height - inputHeight) / 2.0
|
||||
|
||||
self.backgroundLayer.backgroundColor = theme.rootController.navigationSearchBar.inputFillColor.cgColor
|
||||
self.backgroundLayer.cornerRadius = 10.5
|
||||
transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.0, min(1.0, 1.0 - collapseFraction * 1.5)))
|
||||
|
||||
let cancelTextSize = self.cancelButtonTitle.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(Text(
|
||||
text: strings.Common_Cancel,
|
||||
font: Font.regular(17.0),
|
||||
color: theme.rootController.navigationBar.accentTextColor
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 32.0, height: 100.0)
|
||||
)
|
||||
|
||||
let cancelButtonSpacing: CGFloat = 8.0
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight))
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing
|
||||
}
|
||||
transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame)
|
||||
|
||||
transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height)))
|
||||
self.cancelButton.isUserInteractionEnabled = isActiveWithText && !isTablet
|
||||
|
||||
let textX: CGFloat = backgroundFrame.minX + sideInset
|
||||
let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
||||
|
||||
let placeholderSize = self.placeholderContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
)
|
||||
if let placeholderContentView = self.placeholderContent.view {
|
||||
if placeholderContentView.superview == nil {
|
||||
placeholderContentView.alpha = 0.0
|
||||
placeholderContentView.isHidden = true
|
||||
self.addSubview(placeholderContentView)
|
||||
}
|
||||
let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.midY - placeholderSize.height / 2.0), size: placeholderSize)
|
||||
transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame)
|
||||
transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let titleSize = self.titleContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
@@ -379,51 +364,96 @@ final class AddressBarContentComponent: Component {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: size.width - 36.0, height: size.height)
|
||||
)
|
||||
var titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.midX - titleSize.width / 2.0, y: backgroundFrame.midY - titleSize.height / 2.0), size: titleSize)
|
||||
if isSecure && !isActiveWithText {
|
||||
titleContentFrame.origin.x += 7.0
|
||||
|
||||
let expandedBackgroundWidth = size.width - 14.0 * 2.0
|
||||
let collapsedBackgroundWidth = titleSize.width + 32.0
|
||||
var backgroundSize = CGSize(width: expandedBackgroundWidth * (1.0 - collapseFraction) + collapsedBackgroundWidth * collapseFraction, height: 44.0)
|
||||
|
||||
let cancelButtonSpacing: CGFloat = 8.0
|
||||
let cancelSize = self.cancelButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
GlassBarButtonComponent(
|
||||
size: CGSize(width: 44.0, height: 44.0),
|
||||
backgroundColor: nil,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
state: .glass,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(name: "Navigation/Close", tintColor: theme.chat.inputPanel.panelControlColor)
|
||||
)),
|
||||
action: { [weak self] _ in
|
||||
self?.cancelPressed()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
||||
)
|
||||
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundSize.width -= cancelSize.width + cancelButtonSpacing + 4.0
|
||||
}
|
||||
var titleSizeChanged = false
|
||||
self.backgroundView.update(size: backgroundSize, cornerRadius: backgroundSize.height * 0.5, isDark: theme.overallDarkAppearance, tintColor: .init(kind: .panel, color: UIColor(white: theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.6)), isInteractive: true, transition: transition)
|
||||
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: floor((size.width - backgroundSize.width) / 2.0), y: topInset), size: backgroundSize)
|
||||
if isActiveWithText && !isTablet {
|
||||
backgroundFrame.origin.x = 16.0
|
||||
}
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
|
||||
transition.setFrame(view: self.clippingView, frame: CGRect(origin: .zero, size: backgroundFrame.size))
|
||||
transition.setCornerRadius(layer: self.clippingView.layer, cornerRadius: backgroundFrame.size.height * 0.5)
|
||||
|
||||
let textX: CGFloat = sideInset
|
||||
let textFrame = CGRect(origin: CGPoint(x: textX, y: 0.0), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height))
|
||||
|
||||
let placeholderSize = self.placeholderContent.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
Text(text: strings.WebBrowser_AddressPlaceholder, font: Font.regular(17.0), color: theme.rootController.navigationSearchBar.inputPlaceholderTextColor)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: size
|
||||
)
|
||||
if let placeholderContentView = self.placeholderContent.view {
|
||||
if placeholderContentView.superview == nil {
|
||||
placeholderContentView.alpha = 0.0
|
||||
placeholderContentView.isHidden = true
|
||||
self.backgroundView.contentView.addSubview(placeholderContentView)
|
||||
}
|
||||
let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.size.height / 2.0 - placeholderSize.height / 2.0), size: placeholderSize)
|
||||
transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame)
|
||||
transition.setAlpha(view: placeholderContentView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let titleContentFrame = CGRect(origin: CGPoint(x: isActiveWithText ? textFrame.minX : backgroundFrame.width / 2.0 - titleSize.width / 2.0, y: backgroundFrame.height / 2.0 - titleSize.height / 2.0), size: titleSize)
|
||||
if let titleContentView = self.titleContent.view {
|
||||
if titleContentView.superview == nil {
|
||||
self.addSubview(titleContentView)
|
||||
}
|
||||
if titleContentView.frame.width != titleContentFrame.size.width {
|
||||
titleSizeChanged = true
|
||||
self.backgroundView.contentView.addSubview(titleContentView)
|
||||
}
|
||||
transition.setPosition(view: titleContentView, position: titleContentFrame.center)
|
||||
titleContentView.bounds = CGRect(origin: .zero, size: titleContentFrame.size)
|
||||
transition.setAlpha(view: titleContentView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
if let image = self.iconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: titleContentFrame.minX - image.size.width - 3.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
var iconTransition = transition
|
||||
if titleSizeChanged {
|
||||
iconTransition = .immediate
|
||||
}
|
||||
iconTransition.setFrame(view: self.iconView, frame: iconFrame)
|
||||
transition.setAlpha(view: self.iconView, alpha: isActiveWithText || !isSecure ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
|
||||
if let image = self.clearIconView.image {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.width - image.size.width - 4.0, y: floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size)
|
||||
transition.setFrame(view: self.clearIconView, frame: iconFrame)
|
||||
transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0))
|
||||
transition.setAlpha(view: self.clearIconView, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
self.clearIconButton.isUserInteractionEnabled = isActiveWithText
|
||||
}
|
||||
|
||||
if let cancelButtonTitleComponentView = self.cancelButtonTitle.view {
|
||||
if cancelButtonTitleComponentView.superview == nil {
|
||||
self.addSubview(cancelButtonTitleComponentView)
|
||||
cancelButtonTitleComponentView.isUserInteractionEnabled = false
|
||||
if let cancelButtonView = self.cancelButton.view {
|
||||
if cancelButtonView.superview == nil {
|
||||
self.addSubview(cancelButtonView)
|
||||
}
|
||||
transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize))
|
||||
transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0)
|
||||
transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelSize.height) / 2.0)), size: cancelSize))
|
||||
transition.setAlpha(view: cancelButtonView, alpha: isActiveWithText && !isTablet ? 1.0 : 0.0)
|
||||
}
|
||||
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height))
|
||||
let textFieldFrame = CGRect(origin: CGPoint(x: sideInset, y: 0.0), size: CGSize(width: backgroundFrame.width - sideInset, height: backgroundFrame.height))
|
||||
|
||||
let textField: TextField
|
||||
if let current = self.textField {
|
||||
@@ -434,7 +464,7 @@ final class AddressBarContentComponent: Component {
|
||||
textField.autocorrectionType = .no
|
||||
textField.keyboardType = .URL
|
||||
textField.returnKeyType = .go
|
||||
self.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
self.backgroundView.contentView.insertSubview(textField, belowSubview: self.clearIconView)
|
||||
self.textField = textField
|
||||
|
||||
textField.delegate = self
|
||||
@@ -450,9 +480,34 @@ final class AddressBarContentComponent: Component {
|
||||
}
|
||||
|
||||
textField.textColor = theme.rootController.navigationSearchBar.inputTextColor
|
||||
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideInset, y: backgroundFrame.minY - UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height)))
|
||||
transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: sideInset, y: -UIScreenPixel), size: CGSize(width: backgroundFrame.width - sideInset - 32.0, height: backgroundFrame.height)))
|
||||
transition.setAlpha(view: textField, alpha: isActiveWithText ? 1.0 : 0.0)
|
||||
textField.isUserInteractionEnabled = isActiveWithText
|
||||
|
||||
|
||||
let loadingProgressInset: CGFloat = 12.0
|
||||
let loadingProgressSize = CGSize(width: backgroundSize.width - loadingProgressInset * 2.0, height: 2.0)
|
||||
let _ = self.loadingProgress.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(LoadingProgressComponent(
|
||||
color: theme.rootController.navigationBar.accentTextColor,
|
||||
height: loadingProgressSize.height,
|
||||
value: params.loadingProgress ?? 0.0
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: loadingProgressSize
|
||||
)
|
||||
if let loadingProgressView = self.loadingProgress.view {
|
||||
if loadingProgressView.superview == nil {
|
||||
self.clippingView.addSubview(loadingProgressView)
|
||||
}
|
||||
transition.setFrame(view: loadingProgressView, frame: CGRect(origin: CGPoint(x: loadingProgressInset, y: backgroundSize.height - loadingProgressSize.height), size: loadingProgressSize))
|
||||
transition.setAlpha(view: loadingProgressView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
|
||||
self.readingProgressView.backgroundColor = theme.rootController.navigationBar.primaryTextColor.withMultipliedAlpha(0.07)
|
||||
self.readingProgressView.frame = CGRect(origin: .zero, size: CGSize(width: backgroundSize.width * params.readingProgress, height: backgroundSize.height))
|
||||
transition.setAlpha(view: self.readingProgressView, alpha: isActiveWithText ? 0.0 : 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ final class BrowserAddressListComponent: Component {
|
||||
let insets: UIEdgeInsets
|
||||
let metrics: LayoutMetrics
|
||||
let addressBarFrame: CGRect
|
||||
let navigationBarHeight: CGFloat
|
||||
let performAction: ActionSlot<BrowserScreen.Action>
|
||||
let presentInGlobalOverlay: (ViewController) -> Void
|
||||
|
||||
@@ -29,6 +30,7 @@ final class BrowserAddressListComponent: Component {
|
||||
insets: UIEdgeInsets,
|
||||
metrics: LayoutMetrics,
|
||||
addressBarFrame: CGRect,
|
||||
navigationBarHeight: CGFloat,
|
||||
performAction: ActionSlot<BrowserScreen.Action>,
|
||||
presentInGlobalOverlay: @escaping (ViewController) -> Void
|
||||
) {
|
||||
@@ -38,6 +40,7 @@ final class BrowserAddressListComponent: Component {
|
||||
self.insets = insets
|
||||
self.metrics = metrics
|
||||
self.addressBarFrame = addressBarFrame
|
||||
self.navigationBarHeight = navigationBarHeight
|
||||
self.performAction = performAction
|
||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||
}
|
||||
@@ -61,6 +64,9 @@ final class BrowserAddressListComponent: Component {
|
||||
if lhs.addressBarFrame != rhs.addressBarFrame {
|
||||
return false
|
||||
}
|
||||
if lhs.navigationBarHeight != rhs.navigationBarHeight {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -214,15 +220,16 @@ final class BrowserAddressListComponent: Component {
|
||||
var validIds: [AnyHashable] = []
|
||||
var validSectionHeaders: [AnyHashable] = []
|
||||
var sectionOffset: CGFloat = 0.0
|
||||
let headerOffset: CGFloat = self.scrollView.frame.minY
|
||||
|
||||
let sideInset: CGFloat = 0.0
|
||||
let containerInset: CGFloat = 0.0
|
||||
let containerInset: CGFloat = self.scrollView.frame.minY
|
||||
|
||||
for sectionIndex in 0 ..< itemLayout.sections.count {
|
||||
let section = itemLayout.sections[sectionIndex]
|
||||
|
||||
do {
|
||||
var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
||||
var sectionHeaderFrame = CGRect(origin: CGPoint(x: sideInset, y: headerOffset + sectionOffset - self.scrollView.bounds.minY), size: CGSize(width: itemLayout.containerSize.width, height: section.insets.top))
|
||||
|
||||
let sectionHeaderMinY = topOffset + containerInset
|
||||
let sectionHeaderMaxY = containerInset + sectionOffset - self.scrollView.bounds.minY + section.totalHeight - 28.0
|
||||
@@ -540,7 +547,7 @@ final class BrowserAddressListComponent: Component {
|
||||
let containerFrame: CGRect
|
||||
if component.metrics.isTablet {
|
||||
let containerSize = CGSize(width: component.addressBarFrame.width + 32.0, height: 540.0)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 72.0), size: containerSize)
|
||||
containerFrame = CGRect(origin: CGPoint(x: floor(component.addressBarFrame.center.x - containerSize.width / 2.0), y: 100.0), size: containerSize)
|
||||
|
||||
self.backgroundView.layer.cornerRadius = 10.0
|
||||
} else {
|
||||
@@ -602,23 +609,28 @@ final class BrowserAddressListComponent: Component {
|
||||
let itemLayout = ItemLayout(containerSize: containerFrame.size, insets: .zero, sections: sections)
|
||||
self.itemLayout = itemLayout
|
||||
|
||||
let containerWidth = containerFrame.size.width
|
||||
let scrollContentHeight = max(itemLayout.contentHeight, containerFrame.size.height)
|
||||
|
||||
self.ignoreScrolling = true
|
||||
transition.setFrame(view: self.scrollView, frame: CGRect(origin: .zero, size: containerFrame.size))
|
||||
let contentSize = CGSize(width: containerWidth, height: scrollContentHeight)
|
||||
let scrollFrame: CGRect
|
||||
if component.metrics.isTablet {
|
||||
scrollFrame = CGRect(origin: .zero, size: containerFrame.size)
|
||||
} else {
|
||||
scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: component.navigationBarHeight), size: CGSize(width: containerFrame.size.width, height: containerFrame.size.height - component.navigationBarHeight))
|
||||
}
|
||||
transition.setFrame(view: self.scrollView, frame: scrollFrame)
|
||||
let contentSize = CGSize(width: scrollFrame.width, height: scrollContentHeight)
|
||||
if contentSize != self.scrollView.contentSize {
|
||||
self.scrollView.contentSize = contentSize
|
||||
}
|
||||
if resetScrolling {
|
||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: containerWidth, height: containerFrame.size.height))
|
||||
self.scrollView.bounds = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: scrollFrame.size)
|
||||
}
|
||||
self.ignoreScrolling = false
|
||||
self.updateScrolling(transition: transition)
|
||||
|
||||
transition.setFrame(view: self.backgroundView, frame: containerFrame)
|
||||
transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: containerWidth, height: scrollContentHeight)))
|
||||
transition.setFrame(view: self.itemContainerView, frame: CGRect(origin: .zero, size: CGSize(width: scrollFrame.width, height: scrollContentHeight)))
|
||||
|
||||
if component.metrics.isTablet {
|
||||
transition.setFrame(view: self.shadowView, frame: containerFrame.insetBy(dx: -60.0, dy: -60.0))
|
||||
|
||||