Compare commits
421 Commits
window_bla
...
v6.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d51195a537 | ||
|
|
10bedad9bf | ||
|
|
4027d3c07f | ||
|
|
ec78d3b83b | ||
|
|
acf61c24bd | ||
|
|
338183e647 | ||
|
|
59e0de85e3 | ||
|
|
2e6941c23f | ||
|
|
e1c33aa87b | ||
|
|
70d8cede9b | ||
|
|
7587bbd4bc | ||
|
|
668f0498ed | ||
|
|
32ff331744 | ||
|
|
bc90463893 | ||
|
|
7fb486d8d2 | ||
|
|
538df0e67c | ||
|
|
60bb93170e | ||
|
|
766370221b | ||
|
|
8007e6d354 | ||
|
|
443053b927 | ||
|
|
b6000bc1a8 | ||
|
|
8bdf8d42d9 | ||
|
|
c084396f8f | ||
|
|
b29877554c | ||
|
|
a2d17f6f3f | ||
|
|
54018aec90 | ||
|
|
2699f9bcf6 | ||
|
|
094890622a | ||
|
|
bee4ec5ddf | ||
|
|
29f533b170 | ||
|
|
296c2a7b5b | ||
|
|
c4decce7f0 | ||
|
|
a396f507f8 | ||
|
|
eb96fd1b97 | ||
|
|
8cc7d80121 | ||
|
|
d59139202d | ||
|
|
a3cd4a55de | ||
|
|
0cb7388ee0 | ||
|
|
2700e9b326 | ||
|
|
d36d5b2980 | ||
|
|
028437549d | ||
|
|
1f17551bf4 | ||
|
|
cd301c9df2 | ||
|
|
ce43aab4cd | ||
|
|
86d1ab00e0 | ||
|
|
93b2a52f34 | ||
|
|
d3da830ef8 | ||
|
|
ec4deb87c6 | ||
|
|
d832a0a186 | ||
|
|
65800e17a5 | ||
|
|
9c5dd452dd | ||
|
|
78940413a0 | ||
|
|
cc2f459f26 | ||
|
|
d1c98eb22a | ||
|
|
1fe9a0ee5a | ||
|
|
80a87f963a | ||
|
|
2c0051b083 | ||
|
|
4a938170e1 | ||
|
|
f3a27c4fcb | ||
|
|
f43b11fd6a | ||
|
|
93d382c416 | ||
|
|
48e4060d62 | ||
|
|
bbc7ef100c | ||
|
|
c22468c943 | ||
|
|
7d538ea080 | ||
|
|
aad1ff95be | ||
|
|
72f0fb6892 | ||
|
|
a0baad4942 | ||
|
|
5e40d60e34 | ||
|
|
314f787042 | ||
|
|
7f340da0ec | ||
|
|
acafa2bcad | ||
|
|
404d8da1a3 | ||
|
|
26be7840b4 | ||
|
|
520b84f967 | ||
|
|
e7f6a8a476 | ||
|
|
0f3ef4b35c | ||
|
|
60d88693ec | ||
|
|
ff6e2b6d31 | ||
|
|
bc52ad7bf2 | ||
|
|
daa24de171 | ||
|
|
68f0e25227 | ||
|
|
320f2bea7b | ||
|
|
666692e341 | ||
|
|
304cc33b76 | ||
|
|
e0f4aca336 | ||
|
|
ccdff5baef | ||
|
|
16fe056c99 | ||
|
|
fb5c155f7e | ||
|
|
eccbb0df7a | ||
|
|
0bcfcb4ea4 | ||
|
|
b6b4d5c387 | ||
|
|
bb7bfe95e5 | ||
|
|
ac1e5a08d8 | ||
|
|
4a05f0fd05 | ||
|
|
f1dff4d0b7 | ||
|
|
96343603cd | ||
|
|
9dccab915d | ||
|
|
0decf928f0 | ||
|
|
44faef3027 | ||
|
|
52a590334c | ||
|
|
8ea9709305 | ||
|
|
751c7e05f9 | ||
|
|
ebe0aa27e4 | ||
|
|
4ef7f0dd24 | ||
|
|
4779c036f5 | ||
|
|
1480a4e270 | ||
|
|
6a167a4b0f | ||
|
|
9b43f57dfa | ||
|
|
fc74840b55 | ||
|
|
f408b1bb11 | ||
|
|
7a3e16b92f | ||
|
|
939045d606 | ||
|
|
b3ee80e495 | ||
|
|
1c17a76b80 | ||
|
|
08a55721bf | ||
|
|
42991bfa2c | ||
|
|
46897689d3 | ||
|
|
9b3acf7162 | ||
|
|
4436e3ba69 | ||
|
|
78b3f21e7a | ||
|
|
e2ae0a7a4f | ||
|
|
5ed8f0baa5 | ||
|
|
f1d4e48c59 | ||
|
|
48ac431134 | ||
|
|
d6a27c04b9 | ||
|
|
388e9cec09 | ||
|
|
b01f70176b | ||
|
|
e7b4a5db61 | ||
|
|
d5a1d28779 | ||
|
|
e0da4159ae | ||
|
|
0eef766ea1 | ||
|
|
aebd53363c | ||
|
|
f18bb1c780 | ||
|
|
eecb7c0622 | ||
|
|
84c0b46266 | ||
|
|
fb030b3520 | ||
|
|
42aac81fd8 | ||
|
|
09175d2b6b | ||
|
|
46f64f75c7 | ||
|
|
9b5425de14 | ||
|
|
7b1135ecd4 | ||
|
|
2ca63ae324 | ||
|
|
5b02323487 | ||
|
|
cf99eef9c9 | ||
|
|
c54e05d220 | ||
|
|
a651853534 | ||
|
|
a1a81b812a | ||
|
|
e74d0ef4e2 | ||
|
|
ca4caec819 | ||
|
|
900c12d1d0 | ||
|
|
4d61701790 | ||
|
|
4fd0e734ea | ||
|
|
b4d1ba07a6 | ||
|
|
6974c511ea | ||
|
|
d30dd7450f | ||
|
|
f877e653e9 | ||
|
|
699ffc8b50 | ||
|
|
135b81bbb4 | ||
|
|
d9f5c2d16e | ||
|
|
2e0f04ce93 | ||
|
|
8ebb084f0f | ||
|
|
f2bc07458d | ||
|
|
b4daca860f | ||
|
|
ac6919680a | ||
|
|
c6749f2d5c | ||
|
|
47fd936306 | ||
|
|
147439ad34 | ||
|
|
1a3eb0f209 | ||
|
|
acd2c514ce | ||
|
|
fccff0c03a | ||
|
|
7cf8483f9a | ||
|
|
63bad22ac4 | ||
|
|
b009f06375 | ||
|
|
8aa5ebf621 | ||
|
|
53ac770772 | ||
|
|
fbfe47528b | ||
|
|
71443f7def | ||
|
|
a1b1165039 | ||
|
|
c060243201 | ||
|
|
93f84e2b65 | ||
|
|
9cd971a53f | ||
|
|
b22b819683 | ||
|
|
8779ac3ec3 | ||
|
|
16ec935feb | ||
|
|
e59d229097 | ||
|
|
9028d9a339 | ||
|
|
6999a1b5e4 | ||
|
|
7ef6d33ea9 | ||
|
|
3824f19ce5 | ||
|
|
930268ed0d | ||
|
|
cb0a3fdb6e | ||
|
|
4c0a2b1d3d | ||
|
|
6fd34dee5a | ||
|
|
1c56c4dfe5 | ||
|
|
53321160c4 | ||
|
|
5b29d3bb72 | ||
|
|
4796a3116a | ||
|
|
6183569dc2 | ||
|
|
3296a2addb | ||
|
|
235cb0c574 | ||
|
|
570fbd9a00 | ||
|
|
e205b521fd | ||
|
|
ce15754575 | ||
|
|
18085bae70 | ||
|
|
61536b42c9 | ||
|
|
532f166b22 | ||
|
|
c1d3a78316 | ||
|
|
e524d9694d | ||
|
|
1efa889913 | ||
|
|
c73788da24 | ||
|
|
5ec277b2ed | ||
|
|
e327e1bc9c | ||
|
|
37c5c1dc98 | ||
|
|
156e1a00d3 | ||
|
|
3aa9a2f2c0 | ||
|
|
e1f5b57fd3 | ||
|
|
cf757b6c7b | ||
|
|
bd2da8d632 | ||
|
|
01b7e387eb | ||
|
|
60bf6b1f70 | ||
|
|
7839c6294c | ||
|
|
20cadfad29 | ||
|
|
2b93362a57 | ||
|
|
5476f297e1 | ||
|
|
b79fbe2123 | ||
|
|
d9a76fdd9a | ||
|
|
721a69f2b1 | ||
|
|
a49b3e67d0 | ||
|
|
297600ca6d | ||
|
|
15019051c0 | ||
|
|
2b7e8bf449 | ||
|
|
928798dc16 | ||
|
|
1a01ec17d6 | ||
|
|
69da4d749b | ||
|
|
b1fc95b1d9 | ||
|
|
0b7f104c95 | ||
|
|
912974cb80 | ||
|
|
9bbb8ef31c | ||
|
|
e01f53a58c | ||
|
|
82c69a03ef | ||
|
|
f8aa79aaf5 | ||
|
|
700ae79b93 | ||
|
|
8dd8ab6366 | ||
|
|
28d19a99e1 | ||
|
|
6627de6460 | ||
|
|
795d452493 | ||
|
|
2d7f52972d | ||
|
|
6229c0020c | ||
|
|
8db1bf9052 | ||
|
|
2740c5db23 | ||
|
|
512e6de39b | ||
|
|
e3c328c7e3 | ||
|
|
9eb5ea599e | ||
|
|
929032d598 | ||
|
|
09fb42291d | ||
|
|
e6665a8305 | ||
|
|
a785d83791 | ||
|
|
08a64f0dc0 | ||
|
|
0babad837d | ||
|
|
fb1f70f5de | ||
|
|
d4dd1b7ba0 | ||
|
|
82c921d739 | ||
|
|
180a570f99 | ||
|
|
9960a7a7e4 | ||
|
|
2164d08c3c | ||
|
|
4b2d0d1a67 | ||
|
|
d0341d191b | ||
|
|
10237a2d90 | ||
|
|
59390ddaa1 | ||
|
|
312c94f386 | ||
|
|
d697677fab | ||
|
|
bf484b5518 | ||
|
|
74b530259d | ||
|
|
4052c3d101 | ||
|
|
86262333a4 | ||
|
|
3419b49381 | ||
|
|
ec5725d8de | ||
|
|
9df98352a8 | ||
|
|
c0c5ad21b6 | ||
|
|
4a23da6ce1 | ||
|
|
6d1e5243d8 | ||
|
|
c52a31384e | ||
|
|
36447eb1e3 | ||
|
|
811dfee42c | ||
|
|
b075bb9f4e | ||
|
|
00919f3232 | ||
|
|
6dcd52deb5 | ||
|
|
edc70e9acb | ||
|
|
4ad12b6862 | ||
|
|
d63f3b6da1 | ||
|
|
84c70a0905 | ||
|
|
8f65a7ac19 | ||
|
|
c78262be91 | ||
|
|
cb51238bfc | ||
|
|
87f873c8b6 | ||
|
|
a37528b377 | ||
|
|
51a58182ee | ||
|
|
cc4b768f54 | ||
|
|
b016be6eb5 | ||
|
|
881d73d4ab | ||
|
|
8859e352f9 | ||
|
|
5b0fc5b97b | ||
|
|
76cc59acab | ||
|
|
e792088ceb | ||
|
|
1dc70d5a8d | ||
|
|
e876a0f6bd | ||
|
|
50450de3e4 | ||
|
|
c8abc84c3c | ||
|
|
1f3996032c | ||
|
|
b868cfdca9 | ||
|
|
d51944e6a5 | ||
|
|
4419ba55e8 | ||
|
|
9e8ae54821 | ||
|
|
4ed266780a | ||
|
|
46886b4dcc | ||
|
|
1e2531f0b1 | ||
|
|
0e43fd4d00 | ||
|
|
10448bcc3d | ||
|
|
4f1c2788b8 | ||
|
|
dbaa7b5e67 | ||
|
|
6211b7733d | ||
|
|
0d6ea0845e | ||
|
|
b8e10fb34b | ||
|
|
b9c6e595d7 | ||
|
|
7d701d3e9c | ||
|
|
92d9c3c92b | ||
|
|
7007977891 | ||
|
|
9f5d24bbc9 | ||
|
|
2834a83eec | ||
|
|
eb82473395 | ||
|
|
4a33d3227e | ||
|
|
235456e18e | ||
|
|
1f9e532fbc | ||
|
|
8bc2d3184a | ||
|
|
9ef54d1218 | ||
|
|
7e48cb97b0 | ||
|
|
a226f0b58c | ||
|
|
65fd47a082 | ||
|
|
600b7d1d54 | ||
|
|
b8f4d4877e | ||
|
|
6a114e1275 | ||
|
|
5d3d8640e3 | ||
|
|
f09d2d22eb | ||
|
|
94ccd8f8b6 | ||
|
|
c96ce890d6 | ||
|
|
de55be397b | ||
|
|
1420714cae | ||
|
|
b15305bc69 | ||
|
|
db25b111e0 | ||
|
|
eeea9932ed | ||
|
|
c710e9a54d | ||
|
|
62613e7da1 | ||
|
|
946b597471 | ||
|
|
691c55bedd | ||
|
|
8387969467 | ||
|
|
90d21375c8 | ||
|
|
86daf2a9dc | ||
|
|
90473957cb | ||
|
|
3549c00141 | ||
|
|
1c1d13545b | ||
|
|
f1b3db89fb | ||
|
|
8c77baca6f | ||
|
|
36a40d97a7 | ||
|
|
1eb9b40607 | ||
|
|
05c10e3f57 | ||
|
|
0cf3325655 | ||
|
|
48525de714 | ||
|
|
7e15722eab | ||
|
|
719c209c7b | ||
|
|
d05ad44b84 | ||
|
|
b352c97479 | ||
|
|
063085a6bb | ||
|
|
b4bca16109 | ||
|
|
03770c52fe | ||
|
|
10fe5cdd5d | ||
|
|
57d459b917 | ||
|
|
fe26594f12 | ||
|
|
0d8065fc1f | ||
|
|
a3cdae1e94 | ||
|
|
29d77b649b | ||
|
|
9e190cee81 | ||
|
|
687bfd0f17 | ||
|
|
7d7df4f749 | ||
|
|
958dede319 | ||
|
|
76e814944d | ||
|
|
fbc1d75e9a | ||
|
|
b3c7ce05dc | ||
|
|
feea881e09 | ||
|
|
b978bc4876 | ||
|
|
f84181e7a5 | ||
|
|
313ae0f86c | ||
|
|
a39c018359 | ||
|
|
a09f57d908 | ||
|
|
596828cf78 | ||
|
|
44843aa9cd | ||
|
|
5f930cc4d1 | ||
|
|
7defad5d95 | ||
|
|
707af8a295 | ||
|
|
e0fb9ffbb0 | ||
|
|
d614de6f5e | ||
|
|
86b94b4723 | ||
|
|
5070300050 | ||
|
|
1919546441 | ||
|
|
1c3cd8d44b | ||
|
|
292296266f | ||
|
|
a3e8848bc8 | ||
|
|
ced146fc63 | ||
|
|
6ed79f6a0d | ||
|
|
15e4d86e92 | ||
|
|
5a29a7d2a3 | ||
|
|
034740ce46 | ||
|
|
a2847246e6 | ||
|
|
04479ad660 | ||
|
|
35d2fff593 | ||
|
|
2b93fe9a30 | ||
|
|
5c33a2bd5c | ||
|
|
a28f113105 | ||
|
|
eb7976a2ef | ||
|
|
15db1c0c30 | ||
|
|
2b83c95869 |
2
.github/workflows/docker.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
2
.github/workflows/linux.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
4
.github/workflows/mac.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
macos:
|
||||
name: MacOS
|
||||
runs-on: macos-13
|
||||
runs-on: macos-15-intel
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
path: ${{ env.REPO_NAME }}
|
||||
|
||||
5
.github/workflows/mac_packaged.yml
vendored
@@ -60,7 +60,7 @@ jobs:
|
||||
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
path: ${{ env.REPO_NAME }}
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
run: |
|
||||
brew update
|
||||
brew upgrade || true
|
||||
brew install ada-url autoconf automake boost cmake ffmpeg jpeg-xl libavif libheif libtool openal-soft openh264 openssl opus ninja pkg-config python qt yasm xz
|
||||
brew install ada-url autoconf automake boost cmake ffmpeg@6 jpeg-xl libavif libheif libtool openal-soft openh264 openssl opus ninja pkg-config python qt yasm xz
|
||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
xcodebuild -version > CACHE_KEY.txt
|
||||
@@ -82,6 +82,7 @@ jobs:
|
||||
fi
|
||||
echo "CACHE_KEY=`md5 -q CACHE_KEY.txt`" >> $GITHUB_ENV
|
||||
|
||||
echo "MACOSX_DEPLOYMENT_TARGET=$(grep 'set(QT_SUPPORTED_MIN_MACOS_VERSION' /opt/homebrew/Cellar/qt/6.9.2/lib/cmake/Qt6/Qt6ConfigExtras.cmake | sed -E 's/^.*"(.*)"\)$/\1/')" >> $GITHUB_ENV
|
||||
echo "LibrariesPath=`pwd`" >> $GITHUB_ENV
|
||||
|
||||
curl -o tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master
|
||||
|
||||
30
.github/workflows/master_updater.yml
vendored
@@ -5,33 +5,9 @@ on:
|
||||
types: released
|
||||
|
||||
jobs:
|
||||
updater:
|
||||
User-agent:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SKIP: "0"
|
||||
to_branch: "master"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: desktop-app/action_code_updater@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
if: env.SKIP == '0'
|
||||
- name: Push the code to the master branch.
|
||||
if: env.SKIP == '0'
|
||||
run: |
|
||||
token=${{ secrets.TOKEN_FOR_MASTER_UPDATER }}
|
||||
if [ -z "${token}" ]; then
|
||||
echo "Token is unset. Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
url=https://x-access-token:$token@github.com/$GITHUB_REPOSITORY
|
||||
latest_tag=$(git describe --tags --abbrev=0)
|
||||
echo "Latest tag: $latest_tag"
|
||||
|
||||
git remote set-url origin $url
|
||||
git remote -v
|
||||
git checkout master
|
||||
git merge $latest_tag
|
||||
|
||||
git push origin HEAD:refs/heads/$to_branch
|
||||
echo "Done!"
|
||||
type: "dev-to-master"
|
||||
|
||||
4
.github/workflows/snap.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
sudo lxd waitready
|
||||
|
||||
- name: Free up some disk space.
|
||||
uses: endersonmenezes/free-disk-space@dd13eb48e709830a7ec4e50692e841be6b400d90
|
||||
uses: endersonmenezes/free-disk-space@713d134e243b926eba4a5cce0cf608bfd1efb89a
|
||||
with:
|
||||
remove_android: true
|
||||
remove_dotnet: true
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
stale-issue-message: |
|
||||
Hey there!
|
||||
|
||||
2
.github/workflows/user_agent_updater.yml
vendored
@@ -6,8 +6,6 @@ on:
|
||||
schedule:
|
||||
# At 00:00 on day-of-month 1.
|
||||
- cron: "0 0 1 * *"
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
User-agent:
|
||||
|
||||
2
.github/workflows/win.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
||||
run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Clone.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: recursive
|
||||
path: ${{ env.TBUILD }}\${{ env.REPO_NAME }}
|
||||
|
||||
@@ -45,7 +45,7 @@ Version **1.8.15** was the last that supports older systems
|
||||
* Qt 6 ([LGPL](http://doc.qt.io/qt-6/lgpl.html)) and Qt 5.15 ([LGPL](http://doc.qt.io/qt-5/lgpl.html)) slightly patched
|
||||
* OpenSSL 3.2.1 ([Apache License 2.0](https://www.openssl.org/source/apache-license-2.0.txt))
|
||||
* WebRTC ([New BSD License](https://github.com/desktop-app/tg_owt/blob/master/LICENSE))
|
||||
* zlib 1.2.11 ([zlib License](http://www.zlib.net/zlib_license.html))
|
||||
* zlib ([zlib License](http://www.zlib.net/zlib_license.html))
|
||||
* LZMA SDK 9.20 ([public domain](http://www.7-zip.org/sdk.html))
|
||||
* liblzma ([public domain](http://tukaani.org/xz/))
|
||||
* Google Breakpad ([License](https://chromium.googlesource.com/breakpad/breakpad/+/master/LICENSE))
|
||||
|
||||
@@ -35,7 +35,7 @@ include(cmake/td_mtproto.cmake)
|
||||
include(cmake/td_scheme.cmake)
|
||||
include(cmake/td_tde2e.cmake)
|
||||
include(cmake/td_ui.cmake)
|
||||
include(cmake/generate_appdata_changelog.cmake)
|
||||
include(cmake/generate_appstream_changelog.cmake)
|
||||
|
||||
if (DESKTOP_APP_TEST_APPS)
|
||||
include(cmake/tests.cmake)
|
||||
@@ -363,6 +363,14 @@ PRIVATE
|
||||
calls/group/calls_group_members_row.h
|
||||
calls/group/calls_group_menu.cpp
|
||||
calls/group/calls_group_menu.h
|
||||
calls/group/calls_group_message_encryption.cpp
|
||||
calls/group/calls_group_message_encryption.h
|
||||
calls/group/calls_group_message_field.cpp
|
||||
calls/group/calls_group_message_field.h
|
||||
calls/group/calls_group_messages.cpp
|
||||
calls/group/calls_group_messages.h
|
||||
calls/group/calls_group_messages_ui.cpp
|
||||
calls/group/calls_group_messages_ui.h
|
||||
calls/group/calls_group_panel.cpp
|
||||
calls/group/calls_group_panel.h
|
||||
calls/group/calls_group_rtmp.cpp
|
||||
@@ -523,6 +531,8 @@ PRIVATE
|
||||
data/notify/data_notify_settings.h
|
||||
data/notify/data_peer_notify_settings.cpp
|
||||
data/notify/data_peer_notify_settings.h
|
||||
data/notify/data_peer_notify_volume.cpp
|
||||
data/notify/data_peer_notify_volume.h
|
||||
data/stickers/data_custom_emoji.cpp
|
||||
data/stickers/data_custom_emoji.h
|
||||
data/stickers/data_stickers_set.cpp
|
||||
@@ -634,6 +644,8 @@ PRIVATE
|
||||
data/data_report.h
|
||||
data/data_saved_messages.cpp
|
||||
data/data_saved_messages.h
|
||||
data/data_saved_music.cpp
|
||||
data/data_saved_music.h
|
||||
data/data_saved_sublist.cpp
|
||||
data/data_saved_sublist.h
|
||||
data/data_search_controller.cpp
|
||||
@@ -771,6 +783,8 @@ PRIVATE
|
||||
history/view/controls/history_view_voice_record_bar.h
|
||||
history/view/controls/history_view_webpage_processor.cpp
|
||||
history/view/controls/history_view_webpage_processor.h
|
||||
history/view/media/history_view_birthday_suggestion.cpp
|
||||
history/view/media/history_view_birthday_suggestion.h
|
||||
history/view/media/history_view_call.cpp
|
||||
history/view/media/history_view_call.h
|
||||
history/view/media/history_view_contact.cpp
|
||||
@@ -813,6 +827,8 @@ PRIVATE
|
||||
history/view/media/history_view_poll.h
|
||||
history/view/media/history_view_premium_gift.cpp
|
||||
history/view/media/history_view_premium_gift.h
|
||||
history/view/media/history_view_save_document_action.cpp
|
||||
history/view/media/history_view_save_document_action.h
|
||||
history/view/media/history_view_service_box.cpp
|
||||
history/view/media/history_view_service_box.h
|
||||
history/view/media/history_view_similar_channels.cpp
|
||||
@@ -876,6 +892,8 @@ PRIVATE
|
||||
history/view/history_view_fake_items.h
|
||||
history/view/history_view_group_call_bar.cpp
|
||||
history/view/history_view_group_call_bar.h
|
||||
history/view/history_view_group_members_widget.cpp
|
||||
history/view/history_view_group_members_widget.h
|
||||
history/view/history_view_item_preview.h
|
||||
history/view/history_view_list_widget.cpp
|
||||
history/view/history_view_list_widget.h
|
||||
@@ -900,6 +918,8 @@ PRIVATE
|
||||
history/view/history_view_schedule_box.h
|
||||
history/view/history_view_scheduled_section.cpp
|
||||
history/view/history_view_scheduled_section.h
|
||||
history/view/history_view_self_forwards_tagger.cpp
|
||||
history/view/history_view_self_forwards_tagger.h
|
||||
history/view/history_view_send_action.cpp
|
||||
history/view/history_view_send_action.h
|
||||
history/view/history_view_service_message.cpp
|
||||
@@ -944,6 +964,8 @@ PRIVATE
|
||||
history/history_inner_widget.h
|
||||
history/history_location_manager.cpp
|
||||
history/history_location_manager.h
|
||||
history/history_streamed_drafts.cpp
|
||||
history/history_streamed_drafts.h
|
||||
history/history_translation.cpp
|
||||
history/history_translation.h
|
||||
history/history_unread_things.cpp
|
||||
@@ -1046,6 +1068,11 @@ PRIVATE
|
||||
info/reactions_list/info_reactions_list_widget.h
|
||||
info/requests_list/info_requests_list_widget.cpp
|
||||
info/requests_list/info_requests_list_widget.h
|
||||
info/saved/info_saved_music_common.h
|
||||
info/saved/info_saved_music_provider.cpp
|
||||
info/saved/info_saved_music_provider.h
|
||||
info/saved/info_saved_music_widget.cpp
|
||||
info/saved/info_saved_music_widget.h
|
||||
info/saved/info_saved_sublists_widget.cpp
|
||||
info/saved/info_saved_sublists_widget.h
|
||||
info/settings/info_settings_widget.cpp
|
||||
@@ -1119,6 +1146,8 @@ PRIVATE
|
||||
inline_bots/inline_results_widget.h
|
||||
intro/intro_code.cpp
|
||||
intro/intro_code.h
|
||||
intro/intro_email.cpp
|
||||
intro/intro_email.h
|
||||
intro/intro_password_check.cpp
|
||||
intro/intro_password_check.h
|
||||
intro/intro_phone.cpp
|
||||
@@ -1274,6 +1303,8 @@ PRIVATE
|
||||
menu/menu_antispam_validator.h
|
||||
menu/menu_item_download_files.cpp
|
||||
menu/menu_item_download_files.h
|
||||
menu/menu_item_rate_transcribe_session.cpp
|
||||
menu/menu_item_rate_transcribe_session.h
|
||||
menu/menu_mute.cpp
|
||||
menu/menu_mute.h
|
||||
menu/menu_send.cpp
|
||||
@@ -1307,6 +1338,8 @@ PRIVATE
|
||||
mtproto/special_config_request.cpp
|
||||
mtproto/special_config_request.h
|
||||
mtproto/type_utils.h
|
||||
overview/overview_checkbox.cpp
|
||||
overview/overview_checkbox.h
|
||||
overview/overview_layout.cpp
|
||||
overview/overview_layout.h
|
||||
overview/overview_layout_delegate.h
|
||||
@@ -1427,10 +1460,6 @@ PRIVATE
|
||||
platform/platform_window_title.h
|
||||
profile/profile_back_button.cpp
|
||||
profile/profile_back_button.h
|
||||
profile/profile_block_group_members.cpp
|
||||
profile/profile_block_group_members.h
|
||||
profile/profile_block_peer_list.cpp
|
||||
profile/profile_block_peer_list.h
|
||||
profile/profile_block_widget.cpp
|
||||
profile/profile_block_widget.h
|
||||
profile/profile_cover_drop_area.cpp
|
||||
@@ -1625,8 +1654,6 @@ PRIVATE
|
||||
ui/text/format_song_document_name.h
|
||||
ui/widgets/expandable_peer_list.cpp
|
||||
ui/widgets/expandable_peer_list.h
|
||||
ui/widgets/label_with_custom_emoji.cpp
|
||||
ui/widgets/label_with_custom_emoji.h
|
||||
ui/widgets/chat_filters_tabs_strip.cpp
|
||||
ui/widgets/chat_filters_tabs_strip.h
|
||||
ui/widgets/peer_bubble.cpp
|
||||
@@ -1659,6 +1686,8 @@ PRIVATE
|
||||
window/window_adaptive.h
|
||||
window/window_chat_preview.cpp
|
||||
window/window_chat_preview.h
|
||||
window/window_chat_switch_process.cpp
|
||||
window/window_chat_switch_process.h
|
||||
window/window_connecting_widget.cpp
|
||||
window/window_connecting_widget.h
|
||||
window/window_controller.cpp
|
||||
@@ -1780,6 +1809,8 @@ if (WIN32)
|
||||
if (QT_VERSION LESS 6)
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::win_directx_helper)
|
||||
endif()
|
||||
|
||||
target_link_options(Telegram PRIVATE /PDBPAGESIZE:8192)
|
||||
elseif (APPLE)
|
||||
if (NOT DESKTOP_APP_USE_PACKAGED)
|
||||
target_link_libraries(Telegram PRIVATE desktop-app::external_iconv)
|
||||
@@ -2115,7 +2146,7 @@ if (LINUX AND DESKTOP_APP_USE_PACKAGED)
|
||||
include(GNUInstallDirs)
|
||||
configure_file("../lib/xdg/org.telegram.desktop.service" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.service" @ONLY)
|
||||
configure_file("../lib/xdg/org.telegram.desktop.metainfo.xml" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml" @ONLY)
|
||||
generate_appdata_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml")
|
||||
generate_appstream_changelog(Telegram "${CMAKE_SOURCE_DIR}/changelog.txt" "${CMAKE_CURRENT_BINARY_DIR}/org.telegram.desktop.metainfo.xml")
|
||||
install(TARGETS Telegram RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}")
|
||||
install(FILES "Resources/art/icon16.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/16x16/apps" RENAME "org.telegram.desktop.png")
|
||||
install(FILES "Resources/art/icon32.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/32x32/apps" RENAME "org.telegram.desktop.png")
|
||||
|
||||
BIN
Telegram/Resources/animations/cake.tgs
Normal file
BIN
Telegram/Resources/animations/camera_outline.tgs
Normal file
BIN
Telegram/Resources/animations/photo_suggest_icon.tgs
Normal file
BIN
Telegram/Resources/animations/toast/saved_messages.tgs
Normal file
BIN
Telegram/Resources/animations/toast/tagged.tgs
Normal file
BIN
Telegram/Resources/animations/transcribe_loading.tgs
Normal file
BIN
Telegram/Resources/icons/calls/call_message.png
Normal file
|
After Width: | Height: | Size: 692 B |
BIN
Telegram/Resources/icons/calls/call_message@2x.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Telegram/Resources/icons/calls/call_message@3x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
9
Telegram/Resources/icons/chat/new_topic.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / new_topic</title>
|
||||
<g id="Icon-/-Menu-/-new_topic" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M12,3.15416667 C12.3865993,3.15416667 12.7,3.46756734 12.7,3.85416667 C12.7,4.24076599 12.3865993,4.55416667 12,4.55416667 L7.24826389,4.55416667 C5.76035508,4.55416667 4.55416667,5.76035508 4.55416667,7.24826389 L4.55416667,16.7517361 C4.55416667,18.2396449 5.76035508,19.4458333 7.24826389,19.4458333 L16.7517361,19.4458333 C18.2396449,19.4458333 19.4458333,18.2396449 19.4458333,16.7517361 L19.4458333,12 C19.4458333,11.6134007 19.759234,11.3 20.1458333,11.3 C20.5324327,11.3 20.8458333,11.6134007 20.8458333,12 L20.8458333,16.7517361 C20.8458333,19.0128436 19.0128436,20.8458333 16.7517361,20.8458333 L7.24826389,20.8458333 C4.98715643,20.8458333 3.15416667,19.0128436 3.15416667,16.7517361 L3.15416667,7.24826389 C3.15416667,4.98715643 4.98715643,3.15416667 7.24826389,3.15416667 L12,3.15416667 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<path d="M19.3511597,5.34872828 C20.2469248,6.24449337 20.2469248,7.69681554 19.3511597,8.59258064 L12.7489306,15.1948097 C12.4577752,15.4859651 12.0995331,15.7011118 11.7057162,15.8213249 L10.029497,16.3329929 C9.10119412,16.6163585 8.11894274,16.0935336 7.83557714,15.1652307 C7.71876473,14.7825544 7.73602986,14.3714738 7.88451862,13.99994 L8.70227653,11.9538276 C8.8286621,11.6375983 9.01800726,11.3503585 9.2588125,11.1095532 L15.5634724,4.80489335 C16.4592375,3.90912826 17.9115597,3.90912826 18.8073247,4.80489335 L19.3511597,5.34872828 Z M18.3612102,6.33867777 L17.8173753,5.79484285 C17.4683442,5.44581176 16.902453,5.44581176 16.5534219,5.79484285 L10.248762,12.0995027 C10.1421189,12.2061458 10.0582655,12.3333529 10.0022944,12.4733983 L9.18453646,14.5195106 C9.15433809,14.59507 9.15082685,14.678672 9.17458316,14.7564974 C9.23221162,14.9452877 9.43197346,15.0516153 9.62076372,14.9939869 L11.296983,14.4823189 C11.4713888,14.4290813 11.63004,14.3338014 11.7589811,14.2048602 L18.3612102,7.60263114 C18.7102413,7.25360006 18.7102413,6.68770886 18.3612102,6.33867777 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
<polygon id="Path" fill="#FFFFFF" fill-rule="nonzero" points="15.5907211 6.54669192 17.5755336 8.53150439 16.5855841 9.52145388 14.6007716 7.53664141"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
7
Telegram/Resources/icons/limits/mini_gift_lock.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Mini / mini_gift_lock</title>
|
||||
<g id="Mini-/-mini_gift_lock" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M34.6666667,26.3333333 C40.1895142,26.3333333 44.6666667,30.8104858 44.6666667,36.3333333 C44.6666667,41.8561808 40.1895142,46.3333333 34.6666667,46.3333333 C29.1438192,46.3333333 24.6666667,41.8561808 24.6666667,36.3333333 C24.6666667,30.8104858 29.1438192,26.3333333 34.6666667,26.3333333 Z M22,3.06666667 C27.8542183,3.06666667 32.6,7.81244832 32.6,13.6666667 L32.5999548,19.4672746 C34.7232323,19.9235567 36.4292218,21.5034006 37.0650949,23.5539779 C36.287246,23.4090943 35.4858038,23.3333333 34.6666667,23.3333333 C27.4869649,23.3333333 21.6666667,29.1536316 21.6666667,36.3333333 C21.6666667,38.3649522 22.1326999,40.2877232 22.9636948,42.0005746 L12.6666667,42 C9.35295817,42 6.66666667,39.3137085 6.66666667,36 L6.66666667,25.3333333 C6.66666667,22.454169 8.69461762,20.048658 11.4000452,19.4672746 L11.4,13.6666667 C11.4,7.81244832 16.1457817,3.06666667 22,3.06666667 Z M34.6666667,29.3061633 C33.7052821,29.3061633 32.9259259,30.0855195 32.9259259,31.0469041 L32.9259259,36.9308449 C32.9259259,37.7182476 33.2996271,38.4589206 33.9329602,38.9267797 L37.3333333,41.3922119 C38.1066056,41.9634476 39.1965447,41.7996646 39.7677804,41.0263923 L39.8381314,40.9238035 C40.323622,40.1593232 40.1416125,39.1383445 39.4019608,38.5919452 L36.4074074,36.4264726 L36.4074074,31.0469041 C36.4074074,30.0855195 35.6280512,29.3061633 34.6666667,29.3061633 Z M22,7.6 C18.6494725,7.6 15.9333333,10.3161392 15.9333333,13.6666667 L15.9326667,19.3326667 L28.0666667,19.3326667 L28.0666667,13.6666667 C28.0666667,10.3161392 25.3505275,7.6 22,7.6 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
7
Telegram/Resources/icons/menu/reorder.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / reorder</title>
|
||||
<g id="Icon-/-Menu-/-reorder" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M20.4060728,15.0078431 C21.2543281,15.0078431 21.9419748,15.6954899 21.9419748,16.5437451 L21.9419748,19.064098 C21.9419748,19.9123532 21.2543281,20.6 20.4060728,20.6 L17.9103341,20.6 C17.0620789,20.6 16.3744321,19.9123532 16.3744321,19.064098 L16.3744321,16.5437451 C16.3744321,15.6954899 17.0620789,15.0078431 17.9103341,15.0078431 L20.4060728,15.0078431 Z M16.6624648,3.4 C18.1998993,3.4 19.4462362,4.64633689 19.4462362,6.18377135 L19.446,10.561 L21.5745455,8.42203928 C21.782239,8.21317523 22.105482,8.18912467 22.339778,8.35046222 L22.4230702,8.41965499 C22.6580423,8.65331023 22.6591098,9.03320772 22.4254545,9.26817977 L19.3380547,12.3729793 C19.0636746,12.6458372 18.6245193,12.6446032 18.3544177,12.3729793 L15.2670178,9.26817977 C15.0333625,9.03320772 15.03443,8.65331023 15.2694021,8.41965499 C15.5043741,8.18599974 15.8842716,8.18706723 16.1179268,8.42203928 L18.246,10.563 L18.2462362,6.18377135 C18.2462362,5.30907859 17.5371576,4.6 16.6624648,4.6 L6.36754271,4.6 C5.49284994,4.6 4.78377135,5.30907859 4.78377135,6.18377135 L4.783,15.007 L5.4316407,15.0078431 C6.27989595,15.0078431 6.96754271,15.6954899 6.96754271,16.5437451 L6.96754271,19.064098 C6.96754271,19.9123532 6.27989595,20.6 5.4316407,20.6 L2.93590201,20.6 C2.08764675,20.6 1.4,19.9123532 1.4,19.064098 L1.4,16.5437451 C1.4,15.6954899 2.08764675,15.0078431 2.93590201,15.0078431 L3.583,15.007 L3.58377135,6.18377135 C3.58377135,4.64633689 4.83010824,3.4 6.36754271,3.4 L16.6624648,3.4 Z M12.9188568,15.0078431 C13.767112,15.0078431 14.4547588,15.6954899 14.4547588,16.5437451 L14.4547588,19.064098 C14.4547588,19.9123532 13.767112,20.6 12.9188568,20.6 L10.4231181,20.6 C9.57486282,20.6 8.88721607,19.9123532 8.88721607,19.064098 L8.88721607,16.5437451 C8.88721607,15.6954899 9.57486282,15.0078431 10.4231181,15.0078431 L12.9188568,15.0078431 Z M20.4060728,16.2078431 L17.9103341,16.2078431 C17.7248206,16.2078431 17.5744321,16.3582316 17.5744321,16.5437451 L17.5744321,19.064098 C17.5744321,19.2496115 17.7248206,19.4 17.9103341,19.4 L20.4060728,19.4 C20.5915864,19.4 20.7419748,19.2496115 20.7419748,19.064098 L20.7419748,16.5437451 C20.7419748,16.3582316 20.5915864,16.2078431 20.4060728,16.2078431 Z M5.4316407,16.2078431 L2.93590201,16.2078431 C2.75038845,16.2078431 2.6,16.3582316 2.6,16.5437451 L2.6,19.064098 C2.6,19.2496115 2.75038845,19.4 2.93590201,19.4 L5.4316407,19.4 C5.61715425,19.4 5.76754271,19.2496115 5.76754271,19.064098 L5.76754271,16.5437451 C5.76754271,16.3582316 5.61715425,16.2078431 5.4316407,16.2078431 Z M12.9188568,16.2078431 L10.4231181,16.2078431 C10.2376045,16.2078431 10.0872161,16.3582316 10.0872161,16.5437451 L10.0872161,19.064098 C10.0872161,19.2496115 10.2376045,19.4 10.4231181,19.4 L12.9188568,19.4 C13.1043703,19.4 13.2547588,19.2496115 13.2547588,19.064098 L13.2547588,16.5437451 C13.2547588,16.3582316 13.1043703,16.2078431 12.9188568,16.2078431 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
7
Telegram/Resources/icons/settings/birthday_add.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / birthday_add</title>
|
||||
<g id="Icon-/-Menu-/-birthday_add" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<path d="M19.4857266,16.2888889 C19.8170975,16.2888889 20.0857266,16.557518 20.0857266,16.8888889 L20.0856667,18.7328889 L21.9305556,18.7333333 C22.2287893,18.7333333 22.4762023,18.9509229 22.5227026,19.2360102 L22.5305556,19.3333333 C22.5305556,19.6647042 22.2619264,19.9333333 21.9305556,19.9333333 L20.0856667,19.9328889 L20.0857266,21.7777778 C20.0857266,22.0760115 19.868137,22.3234245 19.5830498,22.3699248 L19.4857266,22.3777778 C19.1543558,22.3777778 18.8857266,22.1091486 18.8857266,21.7777778 L18.8856667,19.9328889 L17.0416667,19.9333333 C16.7434329,19.9333333 16.4960199,19.7157437 16.4495197,19.4306564 L16.4416667,19.3333333 C16.4416667,19.0019625 16.7102958,18.7333333 17.0416667,18.7333333 L18.8856667,18.7328889 L18.8857266,16.8888889 C18.8857266,16.5906551 19.1033163,16.3432421 19.3884035,16.2967419 L19.4857266,16.2888889 Z M8.41204757,10.3213892 C8.4968615,10.6417222 8.30593555,10.9701587 7.98560254,11.0549726 C6.00141779,11.5803212 4.96111111,12.4866846 4.96111111,13.4472478 C4.96111111,13.5837542 4.98907974,13.7199344 5.04305686,13.8546242 C5.05815315,13.8780132 5.06968916,13.9034625 5.07949993,13.9299664 C5.67337842,15.1862671 8.51737301,16.291625 12,16.291625 C15.9250424,16.291625 19.0388889,14.8875907 19.0388889,13.4472478 C19.0388889,12.5127848 18.0774561,11.638341 16.2036879,11.1068602 C15.8848931,11.0164364 15.6997618,10.684699 15.7901857,10.3659042 C15.8806095,10.0471093 16.2123468,9.86197804 16.5311417,9.95240189 C18.8627692,10.6137512 20.2388889,11.8653615 20.2388889,13.4472478 C20.2388889,13.979393 20.0501074,14.4768826 19.7060685,14.9283577 C19.6140075,14.9115452 19.5205276,14.9027778 19.425,14.9027778 C18.6281077,14.9027778 17.9737164,15.5128973 17.9034658,16.2914966 L17.9034247,16.3114033 C16.4008483,17.0540936 14.3028164,17.491625 12,17.491625 C9.47842329,17.491625 7.20238688,16.967017 5.68481102,16.0915954 L6.42111111,18.7214444 L6.44,18.7222222 L6.44511111,18.8074444 L6.45530551,18.8430624 L6.4709096,18.9292533 C6.76328017,19.9158038 9.09544378,20.8722222 12,20.8722222 C13.262879,20.8722222 14.4175503,20.6914166 15.3434009,20.4059417 C15.6196573,20.6869274 16.004765,20.8611111 16.4305556,20.8611111 L17.2836209,20.8606349 C16.0386964,21.6207641 14.1238391,22.0722222 12,22.0722222 C8.52835644,22.0722222 5.61510021,20.8659524 5.27347003,19.0770457 L3.95558458,14.3602924 C3.82830877,14.0698882 3.76111111,13.7647359 3.76111111,13.4472478 C3.76111111,11.8222874 5.22885268,10.5435228 7.67846416,9.89494413 C7.99879717,9.81013021 8.32723365,10.0010562 8.41204757,10.3213892 Z M12.4583333,7.42777778 C13.380342,7.42777778 14.1277778,8.17521351 14.1277778,9.09722222 L14.1277778,13.0694444 C14.1277778,13.9914532 13.380342,14.7388889 12.4583333,14.7388889 L11.8472222,14.7388889 C10.9252135,14.7388889 10.1777778,13.9914532 10.1777778,13.0694444 L10.1777778,9.09722222 C10.1777778,8.17521351 10.9252135,7.42777778 11.8472222,7.42777778 L12.4583333,7.42777778 Z M12.4583333,8.62777778 L11.8472222,8.62777778 C11.5879552,8.62777778 11.3777778,8.83795521 11.3777778,9.09722222 L11.3777778,13.0694444 C11.3777778,13.3287115 11.5879552,13.5388889 11.8472222,13.5388889 L12.4583333,13.5388889 C12.7176003,13.5388889 12.9277778,13.3287115 12.9277778,13.0694444 L12.9277778,9.09722222 C12.9277778,8.83795521 12.7176003,8.62777778 12.4583333,8.62777778 Z M12.0207549,1.46944444 C12.8988738,1.46944444 14.1277778,3.40600437 14.1277778,4.64408449 C14.1277778,6.06372135 13.4385567,6.82401696 12,6.82401696 C10.3912998,6.82401696 9.40287158,5.20228362 10.2271046,3.79129771 L10.3125678,3.6554701 C10.6068441,3.21972221 11.1748869,2.01278865 11.1599792,2.03824823 C11.3659094,1.68655903 11.6259528,1.46944444 12.0207549,1.46944444 Z M12.1011111,2.85144444 L12.0713457,2.91751396 L11.8811707,3.30172279 L11.6899023,3.66987931 C11.557639,3.91763569 11.4205876,4.1589191 11.3070312,4.32706715 C10.8867233,4.94943561 11.2769618,5.62401696 12,5.62401696 C12.7481114,5.62401696 12.9277778,5.42582286 12.9277778,4.64408449 C12.9277778,4.29594473 12.7310746,3.77237759 12.4219529,3.28525029 C12.3528553,3.1763634 12.281237,3.07447531 12.2108288,2.98385177 L12.1011111,2.85144444 Z" id="Shape" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -377,6 +377,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_intro_fragment_about" = "Get the code for {phone_number} in the Anonymous Numbers section on Fragment.";
|
||||
"lng_intro_fragment_button" = "Open Fragment";
|
||||
|
||||
"lng_intro_email_setup_title" = "Choose a login email";
|
||||
"lng_intro_email_confirm_subtitle" = "Please check your email {email} (don't forget the spam folder) and enter the code we just sent you.";
|
||||
|
||||
"lng_phone_title" = "Your Phone Number";
|
||||
"lng_phone_desc" = "Please confirm your country code\nand enter your phone number.";
|
||||
"lng_phone_to_qr" = "Quick log in using QR code";
|
||||
@@ -512,12 +515,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_notify_global" = "Global settings";
|
||||
"lng_settings_notify_title" = "Notifications for chats";
|
||||
"lng_settings_desktop_notify" = "Desktop notifications";
|
||||
"lng_settings_master_volume_notifications" = "Volume";
|
||||
"lng_settings_native_title" = "System integration";
|
||||
"lng_settings_use_windows" = "Use Windows notifications";
|
||||
"lng_settings_skip_in_focus" = "Respect system Focus mode";
|
||||
"lng_settings_use_native_notifications" = "Use native notifications";
|
||||
"lng_settings_notifications_position" = "Location on the screen";
|
||||
"lng_settings_notifications_count" = "Notifications count";
|
||||
"lng_settings_notifications_display" = "Display for notifications";
|
||||
"lng_settings_notifications_display_default" = "Default";
|
||||
"lng_settings_sound_allowed" = "Allow sound";
|
||||
"lng_settings_alert_windows" = "Flash the taskbar icon";
|
||||
"lng_settings_alert_mac" = "Bounce the Dock icon";
|
||||
@@ -552,12 +558,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_title_private_chats" = "Notifications for private chats";
|
||||
"lng_notification_about_private_chats#one" = "Please note that **{count} chat** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_private_chats#other" = "Please note that **{count} chats** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_private_chats" = "Notifications volume for private chats";
|
||||
"lng_notification_title_groups" = "Notifications for groups";
|
||||
"lng_notification_about_groups#one" = "Please note that **{count} group** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_groups#other" = "Please note that **{count} groups** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_groups" = "Notifications volume for groups";
|
||||
"lng_notification_title_channels" = "Notifications for channels";
|
||||
"lng_notification_about_channels#one" = "Please note that **{count} channel** is listed as an exception and won't be affected by this change.";
|
||||
"lng_notification_about_channels#other" = "Please note that **{count} channels** are listed as exceptions and won't be affected by this change.";
|
||||
"lng_notification_volume_channel" = "Notifications volume for channels";
|
||||
"lng_notification_exceptions_view" = "View exceptions";
|
||||
"lng_notification_enable" = "Enable notifications";
|
||||
"lng_notification_sound" = "Sound";
|
||||
@@ -687,6 +696,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
|
||||
"lng_shortcuts_show_chat_menu" = "Show chat menu";
|
||||
"lng_shortcuts_show_chat_preview" = "Show chat preview";
|
||||
"lng_shortcuts_record_voice_message" = "Record Voice Message";
|
||||
"lng_shortcuts_record_round_message" = "Record Round Message";
|
||||
|
||||
"lng_settings_chat_reactions_title" = "Quick Reaction";
|
||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||
@@ -768,6 +779,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_birthday_contacts" = "Only your contacts can see your birthday. {link}";
|
||||
"lng_settings_birthday_contacts_link" = "Change >";
|
||||
"lng_settings_birthday_saved" = "Your date of birth was updated.";
|
||||
"lng_settings_birthday_suggested" = "Date of birth was suggested to {user}";
|
||||
"lng_settings_birthday_reset" = "Remove";
|
||||
"lng_settings_channel_label" = "Personal channel";
|
||||
"lng_settings_channel_add" = "Add";
|
||||
@@ -975,6 +987,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_color_subscribe" = "Subscribe to {link} to choose a custom color for your name.";
|
||||
"lng_settings_color_changed" = "Your name color has been updated!";
|
||||
"lng_settings_color_changed_channel" = "Your channel color has been updated!";
|
||||
"lng_settings_color_apply" = "Apply Style";
|
||||
|
||||
"lng_suggest_hide_new_title" = "Hide new chats?";
|
||||
"lng_suggest_hide_new_about" = "You are receiving lots of new chats from users who are not in your Contact List.\n\nDo you want to have such chats **automatically muted** and **archived**?";
|
||||
@@ -1238,6 +1251,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_quick_dialog_action_toast_archive_success" = "The chat has been archived.";
|
||||
"lng_quick_dialog_action_toast_unarchive_success" = "The chat has been unarchived.";
|
||||
|
||||
"lng_archive_hint_title" = "This is your Archive";
|
||||
"lng_archive_hint_about" = "Archived chats will remain in the Archive when you receive a new message. {link}";
|
||||
"lng_archive_hint_about_unmuted" = "When you receive a new message, muted chats will remain in the Archive, while unmuted chats will be moved to Chats. {link}";
|
||||
"lng_archive_hint_about_link" = "Tap to change {emoji}";
|
||||
"lng_archive_hint_section_1" = "Archived Chats";
|
||||
"lng_archive_hint_section_1_info" = "Move any chat into your Archive and back by swiping on it.";
|
||||
"lng_archive_hint_section_2" = "Hiding Archive";
|
||||
"lng_archive_hint_section_2_info" = "Hide the Archive from your Main screen by swiping on it.";
|
||||
"lng_archive_hint_section_3" = "Stories";
|
||||
"lng_archive_hint_section_3_info" = "Archive Stories from your contacts separately from chats with them.";
|
||||
"lng_archive_hint_button" = "Got it";
|
||||
|
||||
"lng_settings_generic_subscribe" = "Subscribe to {link} to use this setting.";
|
||||
"lng_settings_generic_subscribe_link" = "Telegram Premium";
|
||||
|
||||
@@ -1581,6 +1606,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_info_birthday_today_years#other" = "{date}\n({count} years old)";
|
||||
"lng_info_birthday_today_label" = "Birthday today";
|
||||
"lng_info_birthday_today" = "{emoji} {date}";
|
||||
"lng_info_notes_label" = "Notes";
|
||||
"lng_info_notes_private" = "only visible to you";
|
||||
"lng_edit_note" = "Edit Note";
|
||||
"lng_delete_note" = "Delete Note";
|
||||
"lng_info_bio_label" = "Bio";
|
||||
"lng_info_link_label" = "Link";
|
||||
"lng_info_location_label" = "Location";
|
||||
@@ -1604,6 +1633,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_info_group_title" = "Group Info";
|
||||
"lng_info_channel_title" = "Channel Info";
|
||||
"lng_info_topic_title" = "Topic Info";
|
||||
"lng_info_thread_title" = "Thread Info";
|
||||
"lng_profile_enable_notifications" = "Notifications";
|
||||
"lng_profile_send_message" = "Send Message";
|
||||
"lng_profile_open_app" = "Open App";
|
||||
@@ -1618,7 +1648,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_suggest_photo_from_clipboard" = "Suggest From Clipboard";
|
||||
"lng_profile_set_photo_for" = "Set Profile Photo";
|
||||
"lng_profile_set_photo_for_from_clipboard" = "Set From Clipboard";
|
||||
"lng_profile_set_photo_for_about" = "You can replace {user}'s photo with another photo that only you will see.";
|
||||
"lng_profile_photo_reset" = "Reset to Original";
|
||||
"lng_profile_photo_reset_sure" = "Are you sure you want to reset {user}'s photo to the original?";
|
||||
"lng_profile_photo_from_clipboard" = "From clipboard";
|
||||
"lng_profile_suggest_sure" = "You can suggest {user} to set this photo for their Telegram profile.";
|
||||
"lng_profile_suggest_button" = "Suggest";
|
||||
@@ -1638,6 +1670,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_type_audios" = "Voice messages";
|
||||
"lng_media_type_links" = "Shared links";
|
||||
"lng_media_type_rounds" = "Video messages";
|
||||
"lng_media_saved_music_your" = "Your playlist";
|
||||
"lng_media_saved_music_title" = "Playlist";
|
||||
"lng_profile_common_groups_section" = "Groups in common";
|
||||
"lng_info_edit_contact" = "Edit contact";
|
||||
"lng_info_delete_contact" = "Delete contact";
|
||||
@@ -1927,6 +1961,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_manage_monoforum_price" = "Price for each message";
|
||||
"lng_manage_monoforum_about" = "Allow users to send messages to your channel, with the option to charge a fee for each message.";
|
||||
"lng_manage_monoforum_price_about" = "Your channel will receive {percent} of the selected fee ({amount}) for each incoming message.";
|
||||
"lng_manage_monoforum_link_subtitle" = "Link to direct messages";
|
||||
|
||||
"lng_manage_history_visibility_title" = "Chat history for new members";
|
||||
"lng_manage_history_visibility_shown" = "Visible";
|
||||
@@ -2055,6 +2090,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_sure_delete_contact" = "Are you sure you want to delete {contact} from your contact list?";
|
||||
"lng_sure_delete_history" = "Are you sure you want to delete all message history with {contact}?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_group_history" = "Are you sure you want to delete all messages in \"{group}\"?\n\nThis action cannot be undone.";
|
||||
"lng_sure_delete_channel_history" = "Are you sure you want to delete all messages in \"{channel}\"?\n\n**This action cannot be undone.**";
|
||||
"lng_sure_delete_and_exit" = "Are you sure you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
|
||||
"lng_sure_leave_channel" = "Are you sure you want to leave\nthis channel?";
|
||||
"lng_sure_delete_channel" = "Are you sure you want to delete this channel? All subscribers will be removed and all messages will be lost.";
|
||||
@@ -2167,6 +2203,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
|
||||
"lng_action_you_theme_changed" = "You changed the chat theme to {emoji}";
|
||||
"lng_action_theme_changed" = "{from} changed the chat theme to {emoji}";
|
||||
"lng_action_you_gift_theme_changed" = "You set {name} as a new theme for this chat.";
|
||||
"lng_action_gift_theme_changed" = "{from} set {name} as a new theme for this chat.";
|
||||
"lng_action_you_theme_disabled" = "You disabled the chat theme";
|
||||
"lng_action_theme_disabled" = "{from} disabled the chat theme";
|
||||
"lng_action_proximity_distance_m#one" = "{count} meter";
|
||||
@@ -2183,11 +2221,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_gift_upgraded_self_channel" = "You turned this gift to {channel} into a unique collectible";
|
||||
"lng_action_gift_upgraded_mine" = "You turned the gift from {user} into a unique collectible";
|
||||
"lng_action_gift_upgraded_self" = "You turned this gift into a unique collectible";
|
||||
"lng_action_gift_sent_upgrade_other" = "{from} sent an upgrade worth {cost} for the gift you received from {user}.";
|
||||
"lng_action_gift_sent_upgrade_self_other" = "You sent an upgrade worth {cost} for the gift {name} received from {user}.";
|
||||
"lng_action_gift_sent_upgrade" = "{from} sent an upgrade worth {cost} for your gift.";
|
||||
"lng_action_gift_sent_upgrade_self" = "You sent an upgrade worth {cost} for this gift.";
|
||||
"lng_action_gift_sent_upgrade_self_channel" = "You sent an upgrade worth {cost} for your gift to {name}.";
|
||||
"lng_action_gift_upgraded_helped" = "{user} unpacked the gift that you helped to upgrade.";
|
||||
"lng_action_gift_upgraded_helped_self" = "You unpacked the gift that {user} helped to upgrade.";
|
||||
"lng_action_gift_transferred" = "{user} transferred you a gift";
|
||||
"lng_action_gift_transferred_channel" = "{user} transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_unknown" = "Someone transferred you a gift";
|
||||
"lng_action_gift_transferred_unknown_channel" = "Someone transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_self" = "You transferred a unique collectible";
|
||||
"lng_action_gift_displayed_self" = "You've started displaying {name} on your Telegram profile page.";
|
||||
"lng_action_gift_transferred_self_channel" = "You transferred a gift to {channel}";
|
||||
"lng_action_gift_transferred_mine" = "You transferred a gift to {user}";
|
||||
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
|
||||
@@ -2228,6 +2274,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_suggested_video_me" = "You suggested this photo for {user}'s Telegram profile.";
|
||||
"lng_action_suggested_video" = "{user} suggests this photo for your Telegram profile.";
|
||||
"lng_action_suggested_video_button" = "View Photo";
|
||||
"lng_action_suggested_birthday_me" = "You suggest {user} add a date of birth:";
|
||||
"lng_action_suggested_birthday" = "{user} suggests you add your date of birth:";
|
||||
"lng_action_suggested_birtday_button" = "View";
|
||||
"lng_action_attach_menu_bot_allowed" = "You allowed this bot to message you when you added it to your attachment menu.";
|
||||
"lng_action_webapp_bot_allowed" = "You allowed this bot to message you in its web-app.";
|
||||
"lng_action_set_wallpaper_me" = "You set a new wallpaper for this chat";
|
||||
@@ -2248,6 +2297,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_topic_hidden" = "\"{topic}\" was hidden";
|
||||
"lng_action_topic_unhidden" = "\"{topic}\" was unhidden";
|
||||
"lng_action_topic_placeholder" = "topic";
|
||||
"lng_action_topic_bot_thread" = "thread";
|
||||
"lng_action_topic_renamed" = "{from} renamed the {link} to \"{title}\"";
|
||||
"lng_action_topic_icon_changed" = "{from} changed the {link} icon to {emoji}";
|
||||
"lng_action_topic_icon_removed" = "{from} removed the {link} icon";
|
||||
@@ -2339,6 +2389,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_peer_gifts_filter_by_value" = "Sort by Value";
|
||||
"lng_peer_gifts_filter_by_date" = "Sort by Date";
|
||||
"lng_peer_gifts_filter_unlimited" = "Unlimited";
|
||||
"lng_peer_gifts_filter_upgradable" = "Upgradeable";
|
||||
"lng_peer_gifts_filter_limited" = "Limited";
|
||||
"lng_peer_gifts_filter_unique" = "Unique";
|
||||
"lng_peer_gifts_filter_saved" = "Displayed";
|
||||
@@ -2544,6 +2595,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_bot_allow_write_title" = "Allow messaging";
|
||||
"lng_bot_allow_write" = "Do you want to allow this bot to send you messages?";
|
||||
"lng_bot_allow_write_confirm" = "Allow";
|
||||
"lng_bot_new_chat" = "New Chat";
|
||||
"lng_bot_new_thread_title" = "New Thread";
|
||||
"lng_bot_new_thread_about" = "Type any message to create a new thread.";
|
||||
"lng_bot_show_threads_list" = "Show Threads List";
|
||||
|
||||
"lng_attach_failed" = "Failed";
|
||||
"lng_attach_file" = "File";
|
||||
@@ -3548,17 +3603,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_premium_by_stars" = "or {amount}";
|
||||
"lng_gift_stars_subtitle" = "Gift Stars";
|
||||
"lng_gift_stars_about" = "Give {name} gifts that can be kept on your profile or converted to Stars. {link}";
|
||||
"lng_gift_stars_about_collectibles" = "Collectible gifts are unique digital items you can exchange or sell. {link}";
|
||||
"lng_gift_stars_link" = "What are Stars >";
|
||||
"lng_gift_stars_limited" = "limited";
|
||||
"lng_gift_stars_sold_out" = "sold out";
|
||||
"lng_gift_stars_resale" = "resale";
|
||||
"lng_gift_stars_on_sale" = "on sale";
|
||||
"lng_gift_stars_premium" = "premium";
|
||||
"lng_gift_stars_your_left#one" = "{count} left";
|
||||
"lng_gift_stars_your_left#other" = "{count} left";
|
||||
"lng_gift_stars_your_finished" = "none left";
|
||||
"lng_gift_stars_tabs_all" = "All Gifts";
|
||||
"lng_gift_stars_tabs_my" = "My Gifts";
|
||||
"lng_gift_stars_tabs_limited" = "Limited";
|
||||
"lng_gift_stars_tabs_in_stock" = "In Stock";
|
||||
"lng_gift_stars_tabs_resale" = "Resale";
|
||||
"lng_gift_stars_tabs_collectibles" = "Collectibles";
|
||||
"lng_gift_send_title" = "Send a Gift";
|
||||
"lng_gift_send_message" = "Enter Message";
|
||||
"lng_gift_send_anonymous" = "Hide My Name";
|
||||
@@ -3627,12 +3684,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_self_about" = "Buy yourself a gift to display on your page or reserve for later.\n\nLimited-edition gifts upgraded to collectibles can be gifted to others later.";
|
||||
"lng_gift_channel_title" = "Send a Gift";
|
||||
"lng_gift_channel_about" = "Select a gift to show appreciation for {name}.";
|
||||
"lng_gift_released_by" = "Released by {name}";
|
||||
"lng_gift_released_by" = "released by {name}";
|
||||
"lng_gift_unique_owner" = "Owner";
|
||||
"lng_gift_unique_address_copied" = "Address copied to clipboard.";
|
||||
"lng_gift_unique_telegram" = "Telegram";
|
||||
"lng_gift_unique_status" = "Status";
|
||||
"lng_gift_unique_status_non" = "Non-Unique";
|
||||
"lng_gift_unique_upgrade" = "Upgrade";
|
||||
"lng_gift_unique_upgrade_next" = "Upgrade Next Gift";
|
||||
"lng_gift_unique_gift_upgrade" = "Gift an Upgrade";
|
||||
"lng_gift_unique_number" = "Collectible #{index}";
|
||||
"lng_gift_unique_number_by" = "Collectible #{index} by {name}";
|
||||
"lng_gift_unique_model" = "Model";
|
||||
@@ -3642,14 +3702,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_unique_availability_label" = "Quantity";
|
||||
"lng_gift_unique_availability#one" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_availability#other" = "{count} of {amount} issued";
|
||||
"lng_gift_unique_value" = "Value";
|
||||
"lng_gift_unique_value_learn_more" = "learn more";
|
||||
"lng_gift_unique_info" = "Gifted to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_sender" = "Gifted by {from} to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_sender_comment" = "Gifted by {from} to {recipient} on {date} with the comment \"{text}\".";
|
||||
"lng_gift_unique_info_reciever" = "Gifted to {recipient} on {date}.";
|
||||
"lng_gift_unique_info_reciever_comment" = "Gifted to {recipient} on {date} with the comment \"{text}\".";
|
||||
"lng_gift_unique_info_remove_title" = "Remove Description";
|
||||
"lng_gift_unique_info_remove_text" = "Do you want to permanently remove this description from your gift?";
|
||||
"lng_gift_unique_info_remove_confirm" = "Remove for {cost}";
|
||||
"lng_gift_unique_info_removed" = "Removed {name}'s Description!";
|
||||
"lng_gift_availability_left#one" = "{count} of {amount} left";
|
||||
"lng_gift_availability_left#other" = "{count} of {amount} left";
|
||||
"lng_gift_availability_none" = "None of {amount} left";
|
||||
"lng_gift_value_about_average" = "This is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
|
||||
"lng_gift_value_about_last" = "This is the price at which {gift} was last sold on {platform}.";
|
||||
"lng_gift_value_initial_sale" = "Initial Sale";
|
||||
"lng_gift_value_initial_price" = "Initial Price";
|
||||
"lng_gift_value_initial_price_value" = "{stars} ({amount})";
|
||||
"lng_gift_value_last_sale" = "Last Sale";
|
||||
"lng_gift_value_last_price" = "Last Price";
|
||||
"lng_gift_value_minimum_price" = "Minimum Price";
|
||||
"lng_gift_value_minimum_price_tooltip" = "{amount} is the floor price for {gift} gifts listed on Telegram and Fragment.";
|
||||
"lng_gift_vlaue_average_price" = "Average Price";
|
||||
"lng_gift_value_average_price_tooltip" = "{amount} is the average sale price of {gift} gifts on Telegram and Fragment over the past month.";
|
||||
"lng_gift_value_availability#one" = "{count} {emoji} for sale on {platform} {arrow}";
|
||||
"lng_gift_value_availability#other" = "{count} {emoji} for sale on {platform} {arrow}";
|
||||
"lng_gift_value_telegram" = "Telegram";
|
||||
"lng_gift_value_fragment" = "Fragment";
|
||||
"lng_gift_convert_to_stars#one" = "Convert to {count} Star";
|
||||
"lng_gift_convert_to_stars#other" = "Convert to {count} Stars";
|
||||
"lng_gift_convert_sure_title" = "Convert Gift to Stars";
|
||||
@@ -3685,11 +3766,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_preview_about_channel" = "Let the admins of {name} turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_unique_title" = "Unique";
|
||||
"lng_gift_upgrade_unique_about" = "Get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_unique_about_user" = "{name} will get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_unique_about_channel" = "Admins of {name} will get a unique number, model, backdrop and symbol for your gift.";
|
||||
"lng_gift_upgrade_transferable_title" = "Transferable";
|
||||
"lng_gift_upgrade_transferable_about" = "Send your upgraded gift to any of your friends on Telegram.";
|
||||
"lng_gift_upgrade_transferable_about_user" = "{name} will be able to send the gift to anyone on Telegram.";
|
||||
"lng_gift_upgrade_transferable_about_channel" = "Admins of {name} will be able to send the gift to anyone on Telegram.";
|
||||
"lng_gift_upgrade_tradable_title" = "Tradable";
|
||||
"lng_gift_upgrade_tradable_about" = "Sell or auction your gift on third-party NFT marketplaces.";
|
||||
"lng_gift_upgrade_tradable_about_user" = "{name} will be able to sell the gift on Telegram and NFT marketplaces.";
|
||||
"lng_gift_upgrade_tradable_about_channel" = "Admins of {name} will be able to sell the gift on Telegram and NFT marketplaces.";
|
||||
"lng_gift_upgrade_button" = "Upgrade for {price}";
|
||||
"lng_gift_upgrade_decreases" = "Price decreases in {time}";
|
||||
"lng_gift_upgrade_see_table" = "See how this price will decrease {arrow}";
|
||||
"lng_gift_upgrade_prices_about" = "Upgrade cost drops every minute.";
|
||||
"lng_gift_upgrade_prices_title" = "Upgrade Cost";
|
||||
"lng_gift_upgrade_prices_subtitle" = "Users who upgrade their gifts first get collectibles with shorter numbers.";
|
||||
"lng_gift_upgrade_free" = "Upgrade for Free";
|
||||
"lng_gift_upgrade_confirm" = "Confirm";
|
||||
"lng_gift_upgrade_add_my" = "Add my name to the gift";
|
||||
@@ -3698,6 +3790,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_upgrade_add_comment" = "Add sender's name and comment";
|
||||
"lng_gift_upgraded_title" = "Gift Upgraded";
|
||||
"lng_gift_upgraded_about" = "Your gift {name} now has unique attributes and can be transferred to others";
|
||||
"lng_gift_upgrade_gifted_title" = "Upgrade Gifted";
|
||||
"lng_gift_upgrade_gifted_about" = "Now {name} can turn your gift into a unique collectible.";
|
||||
"lng_gift_upgrade_gifted_about_channel" = "Now the admins of {name} can turn your gift into a unique collectible.";
|
||||
"lng_gift_transferred_title" = "Gift Transferred";
|
||||
"lng_gift_transferred_about" = "{name} was successfully transferred to {recipient}.";
|
||||
"lng_gift_transfer_title" = "Transfer {name}";
|
||||
@@ -3724,11 +3819,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_transfer_sure_for" = "Do you want to transfer ownership of {name} to {recipient} for {price}?";
|
||||
"lng_gift_transfer_button" = "Transfer";
|
||||
"lng_gift_transfer_button_for" = "Transfer for {price}";
|
||||
"lng_gift_transfer_set_theme" = "Set as Theme in...";
|
||||
"lng_gift_transfer_choose" = "Choose Chat";
|
||||
"lng_gift_transfer_wear" = "Wear";
|
||||
"lng_gift_transfer_take_off" = "Take Off";
|
||||
"lng_gift_transfer_sell" = "Sell";
|
||||
"lng_gift_transfer_update" = "Change Price";
|
||||
"lng_gift_transfer_unlist" = "Unlist";
|
||||
"lng_gift_transfer_locked_title" = "Action Locked";
|
||||
"lng_gift_transfer_locked_text" = "Transfer this gift to your Telegram account on Fragment to unlock this action.";
|
||||
"lng_gift_sell_unlist_title" = "Unlist {name}";
|
||||
"lng_gift_sell_unlist_sure" = "Are you sure you want to unlist your gift?";
|
||||
"lng_gift_sell_title" = "Price in Stars";
|
||||
@@ -3783,7 +3882,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_resale_symbol" = "Symbol";
|
||||
"lng_gift_resale_symbols#one" = "{count} Symbol";
|
||||
"lng_gift_resale_symbols#other" = "{count} Symbols";
|
||||
"lng_gift_resale_switch_to" = "Switch to {currency}";
|
||||
"lng_gift_resale_switch_to_ton" = "Switch to Ton";
|
||||
"lng_gift_resale_switch_to_stars" = "Switch to Stars";
|
||||
"lng_gift_resale_early" = "You will be able to resell this gift in {duration}.";
|
||||
"lng_gift_transfer_early" = "You will be able to transfer this gift in {duration}.";
|
||||
"lng_gift_resale_transfer_early_title" = "Try Later";
|
||||
@@ -3806,6 +3906,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_collection_delete_sure" = "Are you sure you want to delete this collection?";
|
||||
"lng_gift_collection_delete_button" = "Delete";
|
||||
"lng_gift_collection_add_to" = "Add to Collection";
|
||||
"lng_gift_collection_reorder" = "Reorder";
|
||||
"lng_gift_collection_reorder_exit" = "Apply Reorder";
|
||||
"lng_gift_collection_remove_from" = "Remove from Collection";
|
||||
"lng_gift_locked_title" = "Gift Locked";
|
||||
|
||||
"lng_accounts_limit_title" = "Limit Reached";
|
||||
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";
|
||||
@@ -4124,6 +4228,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_dialogs_suggestions_credits_sub_low_title#other" = "{emoji} {count} Stars needed for {channels}";
|
||||
"lng_dialogs_suggestions_credits_sub_low_about" = "Insufficient funds to cover your subscription.";
|
||||
|
||||
"lng_unconfirmed_auth_title" = "Someone just got access to your messages!";
|
||||
"lng_unconfirmed_auth_confirm" = "Yes, it’s me";
|
||||
"lng_unconfirmed_auth_deny" = "No, it’s not me!";
|
||||
"lng_unconfirmed_auth_single" = "We detected a new login to your account from {from}, {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple#one" = "We detected new {count} login to your account. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple#other" = "We detected new {count} logins to your account. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple_from#one" = "We detected new {count} login to your account from {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_multiple_from#other" = "We detected new {count} logins to your account from {country}. Is it you?";
|
||||
"lng_unconfirmed_auth_denied_title#one" = "New Login Prevented";
|
||||
"lng_unconfirmed_auth_denied_title#other" = "New Logins Prevented";
|
||||
"lng_unconfirmed_auth_denied_single" = "We have terminated the login attempt from {country}.";
|
||||
"lng_unconfirmed_auth_denied_multiple" = "We have terminated the login attempts from: {country}";
|
||||
"lng_unconfirmed_auth_denied_warning" = "Never send your login code to anyone or you can lose your Telegram account!";
|
||||
"lng_unconfirmed_auth_confirmed" = "New Login Allowed";
|
||||
"lng_unconfirmed_auth_confirmed_message" = "You can check the list of your active logins in {link}.";
|
||||
|
||||
"lng_about_random" = "Send a {emoji} emoji to any chat to try your luck.";
|
||||
"lng_about_random_send" = "Send";
|
||||
|
||||
@@ -4274,6 +4394,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_view_group" = "View group info";
|
||||
"lng_context_view_channel" = "View channel info";
|
||||
"lng_context_view_topic" = "View topic info";
|
||||
"lng_context_view_thread" = "View thread info";
|
||||
"lng_context_hide_psa" = "Hide this announcement";
|
||||
"lng_context_pin_to_top" = "Pin";
|
||||
"lng_context_unpin_from_top" = "Unpin";
|
||||
@@ -4291,6 +4412,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_archive_to_list" = "Move to chat list";
|
||||
"lng_context_archive_to_menu_info" = "Archive moved to the main menu!\nRight click the archive button to return the Archive to your chat list.";
|
||||
"lng_context_archive_settings" = "Archive settings";
|
||||
"lng_context_archive_how_does_it_work" = "How does it work?";
|
||||
|
||||
"lng_context_mute" = "Mute notifications";
|
||||
"lng_context_unmute" = "Unmute";
|
||||
@@ -4302,6 +4424,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_remove_from_group" = "Remove from group";
|
||||
"lng_context_add_to_group" = "Add to group";
|
||||
|
||||
"lng_context_rate_transcription" = "Rate transcription";
|
||||
"lng_toast_sent_rate_transcription" = "Thank you for your feedback!";
|
||||
|
||||
"lng_context_copy_link" = "Copy Link";
|
||||
"lng_context_copy_message_link" = "Copy Message Link";
|
||||
"lng_context_copy_post_link" = "Copy Post Link";
|
||||
@@ -4321,6 +4446,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_pack_info" = "View Sticker Set";
|
||||
"lng_context_pack_add" = "Add Stickers";
|
||||
"lng_context_save_file" = "Save As...";
|
||||
"lng_context_save_music_to" = "Save to...";
|
||||
"lng_context_save_music_profile" = "... Profile";
|
||||
"lng_context_save_music_saved" = "... Saved Messages";
|
||||
"lng_context_save_music_folder" = "... Downloads";
|
||||
"lng_context_save_music_about" = "Choose where you want this audio to be saved.";
|
||||
"lng_context_copy_text" = "Copy Text";
|
||||
"lng_context_open_gif" = "Open GIF";
|
||||
"lng_context_save_gif" = "Add to GIFs";
|
||||
@@ -4401,6 +4531,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_add_tag_phrase" = "to messages {arrow}";
|
||||
"lng_add_tag_phrase_long" = "to your Saved Messages {arrow}";
|
||||
"lng_unlock_tags" = "Unlock";
|
||||
"lng_add_tag_selector#one" = "You can add a tag to the message";
|
||||
"lng_add_tag_selector#other" = "You can add a tag to the messages";
|
||||
"lng_message_tagged_with" = "Message tagged with {emoji}";
|
||||
"lng_tagged_view_saved" = "View";
|
||||
|
||||
"lng_context_animated_emoji" = "This message contains emoji from **{name} pack**.";
|
||||
"lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**.";
|
||||
@@ -4682,6 +4816,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_contact_phone_after" = "Phone number will be visible once {user} adds you as a contact.";
|
||||
"lng_contact_share_phone" = "Share my phone number";
|
||||
"lng_contact_phone_will_be_shared" = "You can make your phone visible to {user}.";
|
||||
"lng_contact_add_notes" = "Note";
|
||||
"lng_contact_add_notes_about" = "Notes are only visible to you.";
|
||||
"lng_contact_notes_limit_reached#one" = "You've reached the contact note limit. Please make the note shorter by {count} character.";
|
||||
"lng_contact_notes_limit_reached#other" = "You've reached the contact note limit. Please make the note shorter by {count} characters.";
|
||||
"lng_suggest_photo_for" = "Suggest Photo for {user}";
|
||||
"lng_suggest_birthday" = "Suggest Date of Birth";
|
||||
"lng_suggest_birthday_box_title" = "{user}'s Date of Birth";
|
||||
"lng_suggest_birthday_box_confirm" = "Suggest";
|
||||
"lng_set_photo_for_user" = "Set Photo for {user}";
|
||||
"lng_contact_photo_replace_info" = "You can replace {user}'s photo with another photo that only you will see.";
|
||||
"lng_edit_contact_title" = "Edit contact";
|
||||
"lng_edit_channel_title" = "Edit channel";
|
||||
"lng_edit_bot_title" = "Edit bot";
|
||||
@@ -4735,6 +4879,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
|
||||
"lng_selected_delete_sure#one" = "Do you want to delete {count} message?";
|
||||
"lng_selected_delete_sure#other" = "Do you want to delete {count} messages?";
|
||||
"lng_selected_remove_saved_music" = "Do you want to remove this file from your profile?";
|
||||
"lng_saved_music_added" = "Audio added to your Profile.";
|
||||
"lng_saved_music_removed" = "Audio removed from your Profile.";
|
||||
"lng_delete_photo_sure" = "Do you want to delete this photo?";
|
||||
"lng_delete_for_everyone_hint#one" = "This will delete it for everyone in this chat.";
|
||||
"lng_delete_for_everyone_hint#other" = "This will delete them for everyone in this chat.";
|
||||
@@ -4914,8 +5061,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_payments_webview_no_use" = "Unfortunately, you can't use payments with current system configuration.";
|
||||
"lng_payments_webview_install_edge" = "Please install {link}.";
|
||||
"lng_payments_webview_install_webkit" = "Please install WebKitGTK (webkit2gtk-4.1/webkit2gtk-4.0) using your package manager.";
|
||||
"lng_payments_webview_enable_opengl" = "Please enable OpenGL in application settings.";
|
||||
"lng_payments_webview_switch_x11" = "Unsupported display server. Please switch to X11.";
|
||||
"lng_payments_webview_update_windows" = "Please update your system to Windows 8.1 or later.";
|
||||
"lng_payments_sure_close" = "Are you sure you want to close this payment form? The changes you made will be lost.";
|
||||
"lng_payments_receipt_label" = "Receipt";
|
||||
@@ -5066,6 +5211,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_raised_hand_status" = "wants to speak";
|
||||
"lng_group_call_settings" = "Settings";
|
||||
"lng_group_call_video" = "Video";
|
||||
"lng_group_call_message" = "Message";
|
||||
"lng_group_call_screen_share_start" = "Share Screen";
|
||||
"lng_group_call_screen_share_stop" = "Stop Sharing";
|
||||
"lng_group_call_screen_title" = "Screen {index}";
|
||||
@@ -5127,6 +5273,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_invite_search_results" = "Search results";
|
||||
"lng_group_call_invite_limit" = "This is currently the maximum allowed number of participants.";
|
||||
"lng_group_call_new_muted" = "Mute new participants";
|
||||
"lng_group_call_enable_messages" = "Enable messages";
|
||||
"lng_group_call_speakers" = "Speakers";
|
||||
"lng_group_call_microphone" = "Microphone";
|
||||
"lng_group_call_push_to_talk" = "Push-to-Talk Shortcut";
|
||||
@@ -6080,6 +6227,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_todo_title" = "Checklist";
|
||||
"lng_todo_title_group" = "Group Checklist";
|
||||
"lng_todo_title_user" = "Checklist";
|
||||
"lng_todo_completed#one" = "{count} of {total} completed";
|
||||
"lng_todo_completed#other" = "{count} of {total} completed";
|
||||
"lng_todo_completed_none" = "None of {total} completed";
|
||||
@@ -6260,6 +6408,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_chat_theme_change_wallpaper" = "Change Wallpaper";
|
||||
"lng_chat_theme_title" = "Select theme";
|
||||
"lng_chat_theme_cant_voice" = "Sorry, you can't change the chat theme while you have an unsent voice message.";
|
||||
"lng_chat_theme_gift_replace" = "This gift is already your theme in the chat with {name}. Remove it there and use it here instead?";
|
||||
|
||||
"lng_photo_editor_menu_delete" = "Delete";
|
||||
"lng_photo_editor_menu_flip" = "Flip";
|
||||
@@ -6297,7 +6446,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_sponsored_hide_ads" = "Hide";
|
||||
"lng_sponsored_title" = "What are sponsored messages?";
|
||||
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1_linked" = "Unlike other apps, Telegram never uses your private data to target ads. {link}\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1_link" = "Learn more in the Privacy Policy";
|
||||
"lng_sponsored_info_description1_url" = "https://telegram.org/privacy#5-6-no-ads-based-on-user-data";
|
||||
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together.";
|
||||
"lng_sponsored_info_menu" = "Advertiser info";
|
||||
"lng_sponsored_info_submenu" = "Advertiser: {text}";
|
||||
@@ -6334,6 +6485,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_ringtones_box_title" = "Notification Sound";
|
||||
"lng_ringtones_box_cloud_subtitle" = "Choose your tone";
|
||||
"lng_ringtones_box_volume" = "Volume";
|
||||
"lng_ringtones_box_upload_choose" = "Choose a tone";
|
||||
"lng_ringtones_box_upload_button" = "Upload Sound";
|
||||
"lng_ringtones_box_about" = "Right click on any short voice note or MP3 file in chat and select \"Save for Notifications\". It will appear here.";
|
||||
@@ -6343,6 +6495,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_ringtones_error_max_size" = "Sorry, but your file is too big. The maximum size for ringtones is {size}.";
|
||||
"lng_ringtones_error_max_duration" = "Sorry, but your file is too long. The maximum duration for ringtones is {duration}.";
|
||||
|
||||
"lng_bot_thread_edit" = "Edit Thread";
|
||||
"lng_bot_thread_title" = "Thread Name";
|
||||
"lng_bot_thread_choose_title_and_icon" = "Choose a thread name and icon";
|
||||
|
||||
"lng_forum_topic_new" = "New Topic";
|
||||
"lng_forum_topic_edit" = "Edit Topic";
|
||||
"lng_forum_topic_title" = "Topic Name";
|
||||
|
||||
@@ -51,8 +51,8 @@ var LocationPicker = {
|
||||
},
|
||||
init: function (params) {
|
||||
mapboxgl.accessToken = params.token;
|
||||
if (params.protocol) {
|
||||
mapboxgl.config.API_URL = params.protocol + '://domain/api.mapbox.com';
|
||||
if (location.hostname != 'desktop-app-resource') {
|
||||
mapboxgl.config.API_URL = location.protocol + '//' + location.host + '/api.mapbox.com';
|
||||
}
|
||||
|
||||
var options = { container: 'map', config: {
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
<file alias="topics_list.tgs">../../animations/edit_peers/topics_list.tgs</file>
|
||||
<file alias="direct_messages.tgs">../../animations/edit_peers/direct_messages.tgs</file>
|
||||
<file alias="no_chats.tgs">../../animations/no_chats.tgs</file>
|
||||
<file alias="transcribe_loading.tgs">../../animations/transcribe_loading.tgs</file>
|
||||
<file alias="cake.tgs">../../animations/cake.tgs</file>
|
||||
<file alias="camera_outline.tgs">../../animations/camera_outline.tgs</file>
|
||||
<file alias="photo_suggest_icon.tgs">../../animations/photo_suggest_icon.tgs</file>
|
||||
<file alias="toast/saved_messages.tgs">../../animations/toast/saved_messages.tgs</file>
|
||||
<file alias="toast/tagged.tgs">../../animations/toast/tagged.tgs</file>
|
||||
|
||||
<file alias="dice_idle.tgs">../../animations/dice/dice_idle.tgs</file>
|
||||
<file alias="dart_idle.tgs">../../animations/dice/dart_idle.tgs</file>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="6.0.2.0" />
|
||||
Version="6.2.0.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,0,2,0
|
||||
PRODUCTVERSION 6,0,2,0
|
||||
FILEVERSION 6,2,0,0
|
||||
PRODUCTVERSION 6,2,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "6.0.2.0"
|
||||
VALUE "FileVersion", "6.2.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "6.0.2.0"
|
||||
VALUE "ProductVersion", "6.2.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 6,0,2,0
|
||||
PRODUCTVERSION 6,0,2,0
|
||||
FILEVERSION 6,2,0,0
|
||||
PRODUCTVERSION 6,2,0,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "6.0.2.0"
|
||||
VALUE "FileVersion", "6.2.0.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2025"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "6.0.2.0"
|
||||
VALUE "ProductVersion", "6.2.0.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "core/application.h"
|
||||
#include "core/changelogs.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
@@ -83,7 +86,19 @@ Authorizations::Entry ParseEntry(const MTPDauthorization &data) {
|
||||
} // namespace
|
||||
|
||||
Authorizations::Authorizations(not_null<ApiWrap*> api)
|
||||
: _api(&api->instance()) {
|
||||
: _api(&api->instance())
|
||||
, _autoconfirmPeriod([=] {
|
||||
constexpr auto kFallbackCount = 604800;
|
||||
return api->session().appConfig().get<int>(
|
||||
u"authorization_autoconfirm_period"_q,
|
||||
kFallbackCount);
|
||||
})
|
||||
, _saveUnreviewed([=] {
|
||||
api->session().settings().setUnreviewed(_unreviewed);
|
||||
api->session().saveSettingsDelayed();
|
||||
}) {
|
||||
_unreviewed = api->session().settings().unreviewed();
|
||||
crl::on_main(&api->session(), [=] { removeExpiredUnreviewed(); });
|
||||
Core::App().settings().deviceModelChanges(
|
||||
) | rpl::start_with_next([=](const QString &model) {
|
||||
auto changed = false;
|
||||
@@ -119,6 +134,7 @@ void Authorizations::reload() {
|
||||
) | ranges::views::transform([](const MTPAuthorization &auth) {
|
||||
return ParseEntry(auth.data());
|
||||
}) | ranges::to<List>;
|
||||
removeExpiredUnreviewed();
|
||||
refreshCallsDisabledHereFromCloud();
|
||||
_listChanges.fire({});
|
||||
}).fail([=] {
|
||||
@@ -261,4 +277,129 @@ crl::time Authorizations::lastReceivedTime() {
|
||||
return _lastReceived;
|
||||
}
|
||||
|
||||
const std::vector<Data::UnreviewedAuth> &Authorizations::unreviewed() {
|
||||
removeExpiredUnreviewed();
|
||||
return _unreviewed;
|
||||
}
|
||||
|
||||
void Authorizations::removeExpiredUnreviewed() {
|
||||
const auto now = base::unixtime::now();
|
||||
const auto period = _autoconfirmPeriod();
|
||||
|
||||
const auto oldSize = _unreviewed.size();
|
||||
_unreviewed.erase(
|
||||
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
|
||||
[=](const auto &auth) {
|
||||
return (now - auth.date) >= period;
|
||||
}),
|
||||
_unreviewed.end());
|
||||
|
||||
if (_unreviewed.size() != oldSize) {
|
||||
_saveUnreviewed();
|
||||
}
|
||||
}
|
||||
|
||||
void Authorizations::review(const std::vector<uint64> &hashes, bool confirm) {
|
||||
for (const auto hash : hashes) {
|
||||
if (const auto sent = _reviewRequests.take(hash)) {
|
||||
_api.request(*sent).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
const auto checkComplete = [=] {
|
||||
if (_reviewRequests.empty()) {
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto hash : hashes) {
|
||||
const auto removeFromUnreviewed = [=] {
|
||||
_unreviewed.erase(
|
||||
std::remove_if(_unreviewed.begin(), _unreviewed.end(),
|
||||
[hash](const auto &auth) { return auth.hash == hash; }),
|
||||
_unreviewed.end());
|
||||
_reviewRequests.remove(hash);
|
||||
checkComplete();
|
||||
};
|
||||
|
||||
if (confirm) {
|
||||
using Flag = MTPaccount_ChangeAuthorizationSettings::Flag;
|
||||
const auto id = _api.request(MTPaccount_ChangeAuthorizationSettings(
|
||||
MTP_flags(Flag::f_confirmed),
|
||||
MTP_long(hash),
|
||||
MTPBool(), // encrypted_requests_disabled
|
||||
MTPBool() // call_requests_disabled
|
||||
)).done([=] {
|
||||
removeFromUnreviewed();
|
||||
}).fail([=] {
|
||||
removeFromUnreviewed();
|
||||
}).send();
|
||||
_reviewRequests.emplace(hash, id);
|
||||
} else {
|
||||
const auto id = _api.request(MTPaccount_ResetAuthorization(
|
||||
MTP_long(hash)
|
||||
)).done([=](const MTPBool &result) {
|
||||
if (mtpIsTrue(result)) {
|
||||
_list.erase(
|
||||
ranges::remove(_list, hash, &Entry::hash),
|
||||
end(_list));
|
||||
_listChanges.fire({});
|
||||
}
|
||||
removeFromUnreviewed();
|
||||
}).fail([=] {
|
||||
removeFromUnreviewed();
|
||||
}).send();
|
||||
_reviewRequests.emplace(hash, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> Authorizations::unreviewedChanges() const {
|
||||
return _unreviewedChanges.events();
|
||||
}
|
||||
|
||||
void Authorizations::apply(const MTPUpdate &update) {
|
||||
removeExpiredUnreviewed();
|
||||
update.match([&](const MTPDupdateNewAuthorization &data) {
|
||||
auto unreviewed = Data::UnreviewedAuth{
|
||||
.hash = data.vhash().v,
|
||||
.unconfirmed = data.is_unconfirmed(),
|
||||
.date = data.vdate().value_or_empty(),
|
||||
.device = qs(data.vdevice().value_or_empty()),
|
||||
.location = qs(data.vlocation().value_or_empty())
|
||||
};
|
||||
if (!unreviewed.unconfirmed) {
|
||||
const auto hash = unreviewed.hash;
|
||||
const auto was = _unreviewed.size();
|
||||
_unreviewed.erase(
|
||||
std::remove_if(
|
||||
_unreviewed.begin(),
|
||||
_unreviewed.end(),
|
||||
[hash](const auto &auth) { return auth.hash == hash; }),
|
||||
_unreviewed.end());
|
||||
if (was != _unreviewed.size()) {
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &auth : _unreviewed) {
|
||||
if (auth.hash == unreviewed.hash) {
|
||||
auth = std::move(unreviewed);
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_unreviewed.push_back(std::move(unreviewed));
|
||||
_saveUnreviewed();
|
||||
_unreviewedChanges.fire({});
|
||||
}, [](auto&&) {
|
||||
Unexpected("Update in Authorizations::apply.");
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "data/data_authorization.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class ApiWrap;
|
||||
@@ -35,6 +36,8 @@ public:
|
||||
Fn<void(const MTP::Error &error)> &&fail,
|
||||
std::optional<uint64> hash = std::nullopt);
|
||||
|
||||
void apply(const MTPUpdate &update);
|
||||
|
||||
[[nodiscard]] crl::time lastReceivedTime();
|
||||
|
||||
[[nodiscard]] List list() const;
|
||||
@@ -42,6 +45,11 @@ public:
|
||||
[[nodiscard]] int total() const;
|
||||
[[nodiscard]] rpl::producer<int> totalValue() const;
|
||||
|
||||
[[nodiscard]] const std::vector<Data::UnreviewedAuth> &unreviewed();
|
||||
[[nodiscard]] rpl::producer<> unreviewedChanges() const;
|
||||
|
||||
void review(const std::vector<uint64> &hashes, bool confirm);
|
||||
|
||||
void updateTTL(int days);
|
||||
[[nodiscard]] rpl::producer<int> ttlDays() const;
|
||||
|
||||
@@ -57,6 +65,7 @@ public:
|
||||
|
||||
private:
|
||||
void refreshCallsDisabledHereFromCloud();
|
||||
void removeExpiredUnreviewed();
|
||||
|
||||
MTP::Sender _api;
|
||||
mtpRequestId _requestId = 0;
|
||||
@@ -64,10 +73,16 @@ private:
|
||||
List _list;
|
||||
rpl::event_stream<> _listChanges;
|
||||
|
||||
Fn<int()> _autoconfirmPeriod;
|
||||
std::vector<Data::UnreviewedAuth> _unreviewed;
|
||||
rpl::event_stream<> _unreviewedChanges;
|
||||
Fn<void()> _saveUnreviewed;
|
||||
|
||||
mtpRequestId _ttlRequestId = 0;
|
||||
rpl::variable<int> _ttlDays = 0;
|
||||
|
||||
base::flat_map<uint64, mtpRequestId> _toggleCallsDisabledRequests;
|
||||
base::flat_map<uint64, mtpRequestId> _reviewRequests;
|
||||
rpl::variable<bool> _callsDisabledHere;
|
||||
|
||||
crl::time _lastReceived = 0;
|
||||
|
||||
@@ -142,13 +142,12 @@ void ConfirmSubscriptionBox(
|
||||
const auto content = box->verticalLayout();
|
||||
|
||||
Ui::AddSkip(content, st::confirmInvitePhotoTop);
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::RpWidget>(content)));
|
||||
const auto userpic = userpicWrap->entity();
|
||||
const auto userpic = content->add(
|
||||
object_ptr<Ui::RpWidget>(content),
|
||||
style::al_top);
|
||||
const auto photoSize = st::confirmInvitePhotoSize;
|
||||
userpic->resize(Size(photoSize));
|
||||
userpic->setNaturalWidth(photoSize);
|
||||
const auto creditsIconSize = photoSize / 3;
|
||||
const auto creditsIconCallback =
|
||||
Ui::PaintOutlinedColoredCreditsIconCallback(
|
||||
@@ -188,8 +187,8 @@ void ConfirmSubscriptionBox(
|
||||
}
|
||||
auto p = QPainter(userpic);
|
||||
p.drawImage(0, 0, state->frame);
|
||||
}, userpicWrap->lifetime());
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
}, userpic->lifetime());
|
||||
userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (photo) {
|
||||
state->photoMedia = photo->createMediaView();
|
||||
state->photoMedia->wanted(Data::PhotoSize::Small, Data::FileOrigin());
|
||||
@@ -197,7 +196,7 @@ void ConfirmSubscriptionBox(
|
||||
session->downloaderTaskFinished(
|
||||
) | rpl::start_with_next([=] {
|
||||
userpic->update();
|
||||
}, userpicWrap->entity()->lifetime());
|
||||
}, userpic->lifetime());
|
||||
}
|
||||
} else {
|
||||
state->photoEmpty = std::make_unique<Ui::EmptyUserpic>(
|
||||
@@ -219,7 +218,6 @@ void ConfirmSubscriptionBox(
|
||||
box,
|
||||
tr::lng_channel_invite_subscription_title(),
|
||||
st::inviteLinkSubscribeBoxTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
@@ -234,7 +232,6 @@ void ConfirmSubscriptionBox(
|
||||
Ui::Text::Bold),
|
||||
Ui::Text::WithEntities),
|
||||
st::inviteLinkSubscribeBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
@@ -250,7 +247,6 @@ void ConfirmSubscriptionBox(
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::inviteLinkSubscribeBoxTerms),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
{
|
||||
|
||||
@@ -159,6 +159,7 @@ void MessagesSearch::searchReceived(
|
||||
// Don't apply cached data!
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
_history->peer->processTopics(data.vtopics());
|
||||
}
|
||||
auto items = HistoryItemsFromTL(&owner, data.vmessages().v);
|
||||
const auto total = int(data.vmessages().v.size());
|
||||
@@ -168,6 +169,7 @@ void MessagesSearch::searchReceived(
|
||||
// Don't apply cached data!
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
_history->peer->processTopics(data.vtopics());
|
||||
}
|
||||
auto items = HistoryItemsFromTL(&owner, data.vmessages().v);
|
||||
// data.vnext_rate() is used only in global search.
|
||||
@@ -178,17 +180,14 @@ void MessagesSearch::searchReceived(
|
||||
// Don't apply cached data!
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
}
|
||||
if (const auto channel = _history->peer->asChannel()) {
|
||||
channel->ptsReceived(data.vpts().v);
|
||||
if (_requestId != 0) {
|
||||
// Don't apply cached data!
|
||||
channel->processTopics(data.vtopics());
|
||||
if (const auto channel = _history->peer->asChannel()) {
|
||||
channel->ptsReceived(data.vpts().v);
|
||||
} else {
|
||||
LOG(("API Error: "
|
||||
"received messages.channelMessages when no channel "
|
||||
"was passed!"));
|
||||
}
|
||||
} else {
|
||||
LOG(("API Error: "
|
||||
"received messages.channelMessages when no channel "
|
||||
"was passed!"));
|
||||
_history->peer->processTopics(data.vtopics());
|
||||
}
|
||||
auto items = HistoryItemsFromTL(&owner, data.vmessages().v);
|
||||
const auto total = int(data.vcount().v);
|
||||
|
||||
@@ -22,13 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
|
||||
return TimeId(msgId >> 32);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Polls::Polls(not_null<ApiWrap*> api)
|
||||
: _session(&api->session())
|
||||
|
||||
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "payments/payments_form.h"
|
||||
#include "ui/chat/chat_style.h" // ColorCollectible
|
||||
#include "ui/text/format_values.h"
|
||||
|
||||
namespace Api {
|
||||
@@ -863,7 +864,9 @@ std::optional<Data::StarGift> FromTL(
|
||||
.perUserRemains = data.vper_user_remains().value_or_empty(),
|
||||
.firstSaleDate = data.vfirst_sale_date().value_or_empty(),
|
||||
.lastSaleDate = data.vlast_sale_date().value_or_empty(),
|
||||
.lockedUntilDate = data.vlocked_until_date().value_or_empty(),
|
||||
.requirePremium = data.is_require_premium(),
|
||||
.peerColorAvailable = data.is_peer_color_available(),
|
||||
.upgradable = data.vupgrade_stars().has_value(),
|
||||
.birthday = data.is_birthday(),
|
||||
.soldOut = data.is_sold_out(),
|
||||
@@ -890,27 +893,56 @@ std::optional<Data::StarGift> FromTL(
|
||||
const auto releasedById = data.vreleased_by()
|
||||
? peerFromMTP(*data.vreleased_by())
|
||||
: PeerId();
|
||||
const auto themeUserId = data.vtheme_peer()
|
||||
? peerFromMTP(*data.vtheme_peer())
|
||||
: PeerId();
|
||||
const auto releasedBy = releasedById
|
||||
? session->data().peer(releasedById).get()
|
||||
: nullptr;
|
||||
const auto themeUser = themeUserId
|
||||
? session->data().peer(themeUserId).get()
|
||||
: nullptr;
|
||||
const auto colorCollectible = (data.vpeer_color()
|
||||
&& data.vpeer_color()->type() == mtpc_peerColorCollectible)
|
||||
? std::make_shared<Ui::ColorCollectible>(
|
||||
Data::ParseColorCollectible(
|
||||
data.vpeer_color()->c_peerColorCollectible()))
|
||||
: nullptr;
|
||||
auto result = Data::StarGift{
|
||||
.id = uint64(data.vid().v),
|
||||
.id = data.vid().v,
|
||||
.unique = std::make_shared<Data::UniqueGift>(Data::UniqueGift{
|
||||
.id = data.vid().v,
|
||||
.initialGiftId = data.vgift_id().v,
|
||||
.slug = qs(data.vslug()),
|
||||
.title = qs(data.vtitle()),
|
||||
.giftAddress = qs(data.vgift_address().value_or_empty()),
|
||||
.ownerAddress = qs(data.vowner_address().value_or_empty()),
|
||||
.ownerName = qs(data.vowner_name().value_or_empty()),
|
||||
.ownerId = (data.vowner_id()
|
||||
? peerFromMTP(*data.vowner_id())
|
||||
: PeerId()),
|
||||
.hostId = (data.vhost_id()
|
||||
? peerFromMTP(*data.vhost_id())
|
||||
: PeerId()),
|
||||
.releasedBy = releasedBy,
|
||||
.themeUser = themeUser,
|
||||
.nanoTonForResale = FindTonForResale(data.vresell_amount()),
|
||||
.starsForResale = FindStarsForResale(data.vresell_amount()),
|
||||
.number = data.vnum().v,
|
||||
.onlyAcceptTon = data.is_resale_ton_only(),
|
||||
.canBeTheme = data.is_theme_available(),
|
||||
.model = *model,
|
||||
.pattern = *pattern,
|
||||
.value = (data.vvalue_amount()
|
||||
? std::make_shared<Data::UniqueGiftValue>(
|
||||
Data::UniqueGiftValue{
|
||||
.currency = qs(
|
||||
data.vvalue_currency().value_or_empty()),
|
||||
.valuePrice = int64(
|
||||
data.vvalue_amount().value_or_empty()),
|
||||
})
|
||||
: nullptr),
|
||||
.peerColor = colorCollectible,
|
||||
}),
|
||||
.document = model->document,
|
||||
.releasedBy = releasedBy,
|
||||
@@ -967,10 +999,15 @@ std::optional<Data::SavedStarGift> FromTL(
|
||||
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
|
||||
.starsUpgradedBySender = int64(
|
||||
data.vupgrade_stars().value_or_empty()),
|
||||
.starsForDetailsRemove = int64(
|
||||
data.vdrop_original_details_stars().value_or_empty()),
|
||||
.giftPrepayUpgradeHash = qs(
|
||||
data.vprepaid_upgrade_hash().value_or_empty()),
|
||||
.fromId = (data.vfrom_id()
|
||||
? peerFromMTP(*data.vfrom_id())
|
||||
: PeerId()),
|
||||
.date = data.vdate().v,
|
||||
.upgradeSeparate = data.is_upgrade_separate(),
|
||||
.upgradable = data.is_can_upgrade(),
|
||||
.anonymous = data.is_name_hidden(),
|
||||
.pinned = data.is_pinned_to_top() && hasUnique,
|
||||
|
||||
@@ -25,10 +25,6 @@ namespace {
|
||||
|
||||
constexpr auto kSendTogglesDelay = 3 * crl::time(1000);
|
||||
|
||||
[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
|
||||
return TimeId(msgId >> 32);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TodoLists::TodoLists(not_null<ApiWrap*> api)
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
|
||||
namespace Api {
|
||||
|
||||
@@ -25,6 +26,32 @@ Transcribes::Transcribes(not_null<ApiWrap*> api)
|
||||
, _api(&api->instance()) {
|
||||
}
|
||||
|
||||
bool Transcribes::isRated(not_null<HistoryItem*> item) const {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
return _session->settings().isTranscriptionRated(transcribeId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Transcribes::rate(not_null<HistoryItem*> item, bool isGood) {
|
||||
const auto fullId = item->fullId();
|
||||
for (const auto &[transcribeId, id] : _ids) {
|
||||
if (id == fullId) {
|
||||
_api.request(MTPmessages_RateTranscribedAudio(
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_long(transcribeId),
|
||||
MTP_bool(isGood))).send();
|
||||
_session->settings().markTranscriptionAsRated(transcribeId);
|
||||
_session->saveSettings();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Transcribes::freeFor(not_null<HistoryItem*> item) const {
|
||||
if (const auto channel = item->history()->peer->asMegagroup()) {
|
||||
const auto owner = &channel->owner();
|
||||
|
||||
@@ -37,6 +37,8 @@ public:
|
||||
void apply(const MTPDupdateTranscribedAudio &update);
|
||||
|
||||
[[nodiscard]] bool freeFor(not_null<HistoryItem*> item) const;
|
||||
[[nodiscard]] bool isRated(not_null<HistoryItem*> item) const;
|
||||
void rate(not_null<HistoryItem*> item, bool isGood);
|
||||
|
||||
[[nodiscard]] bool trialsSupport();
|
||||
[[nodiscard]] TimeId trialsRefreshAt();
|
||||
|
||||
@@ -51,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/history_streamed_drafts.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "core/application.h"
|
||||
#include "storage/storage_account.h"
|
||||
@@ -1096,6 +1097,9 @@ void Updates::handleSendActionUpdate(
|
||||
const auto from = (fromId == session().userPeerId())
|
||||
? session().user().get()
|
||||
: session().data().peerLoaded(fromId);
|
||||
const auto when = requestingDifference()
|
||||
? 0
|
||||
: base::unixtime::now();
|
||||
if (action.type() == mtpc_speakingInGroupCallAction) {
|
||||
handleSpeakingInCall(peer, fromId, from);
|
||||
}
|
||||
@@ -1108,10 +1112,11 @@ void Updates::handleSendActionUpdate(
|
||||
const auto &data = action.c_sendMessageEmojiInteractionSeen();
|
||||
handleEmojiInteraction(peer, qs(data.vemoticon()));
|
||||
return;
|
||||
} else if (action.type() == mtpc_sendMessageTextDraftAction) {
|
||||
const auto &data = action.c_sendMessageTextDraftAction();
|
||||
history->streamedDrafts().apply(rootId, fromId, when, data);
|
||||
return;
|
||||
}
|
||||
const auto when = requestingDifference()
|
||||
? 0
|
||||
: base::unixtime::now();
|
||||
session().data().sendActionManager().registerFor(
|
||||
history,
|
||||
rootId,
|
||||
@@ -1951,7 +1956,7 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
auto &d = update.c_updateUserTyping();
|
||||
handleSendActionUpdate(
|
||||
peerFromUser(d.vuser_id()),
|
||||
0,
|
||||
d.vtop_msg_id().value_or_empty(),
|
||||
peerFromUser(d.vuser_id()),
|
||||
d.vaction());
|
||||
} break;
|
||||
@@ -2123,7 +2128,9 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
case mtpc_updateGroupCallParticipants:
|
||||
case mtpc_updateGroupCallChainBlocks:
|
||||
case mtpc_updateGroupCallConnection:
|
||||
case mtpc_updateGroupCall: {
|
||||
case mtpc_updateGroupCall:
|
||||
case mtpc_updateGroupCallMessage:
|
||||
case mtpc_updateGroupCallEncryptedMessage: {
|
||||
Core::App().calls().handleUpdate(&session(), update);
|
||||
} break;
|
||||
|
||||
@@ -2213,6 +2220,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateNewAuthorization: {
|
||||
session().api().authorizations().apply(update);
|
||||
} break;
|
||||
|
||||
case mtpc_updateServiceNotification: {
|
||||
const auto &d = update.c_updateServiceNotification();
|
||||
const auto text = TextWithEntities {
|
||||
@@ -2480,9 +2491,9 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelPinnedTopic: {
|
||||
const auto &d = update.c_updateChannelPinnedTopic();
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
case mtpc_updatePinnedForumTopic: {
|
||||
const auto &d = update.c_updatePinnedForumTopic();
|
||||
const auto peerId = peerFromMTP(d.vpeer());
|
||||
if (const auto peer = session().data().peerLoaded(peerId)) {
|
||||
const auto rootId = d.vtopic_id().v;
|
||||
if (const auto topic = peer->forumTopicFor(rootId)) {
|
||||
@@ -2493,9 +2504,9 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case mtpc_updateChannelPinnedTopics: {
|
||||
const auto &d = update.c_updateChannelPinnedTopics();
|
||||
const auto peerId = peerFromChannel(d.vchannel_id());
|
||||
case mtpc_updatePinnedForumTopics: {
|
||||
const auto &d = update.c_updatePinnedForumTopics();
|
||||
const auto peerId = peerFromMTP(d.vpeer());
|
||||
if (const auto peer = session().data().peerLoaded(peerId)) {
|
||||
if (const auto forum = peer->forum()) {
|
||||
const auto done = [&] {
|
||||
|
||||
@@ -134,7 +134,7 @@ rpl::producer<rpl::no_value, Usernames::Error> Usernames::toggle(
|
||||
if (list.empty()) {
|
||||
if (error == Error::Unknown) {
|
||||
it->second.done.fire_done();
|
||||
} else if (error == Error::TooMuch) {
|
||||
} else {
|
||||
it->second.done.fire_error_copy(error);
|
||||
}
|
||||
_toggleRequests.remove(peerId);
|
||||
@@ -149,6 +149,8 @@ rpl::producer<rpl::no_value, Usernames::Error> Usernames::toggle(
|
||||
const auto type = error.type();
|
||||
if (type == u"USERNAMES_ACTIVE_TOO_MUCH"_q) {
|
||||
pop(Error::TooMuch);
|
||||
} else if (type.startsWith(u"FLOOD_WAIT_"_q)) {
|
||||
pop(Error::Flood);
|
||||
} else {
|
||||
pop(Error::Unknown);
|
||||
}
|
||||
@@ -158,19 +160,19 @@ rpl::producer<rpl::no_value, Usernames::Error> Usernames::toggle(
|
||||
_api.request(MTPaccount_ToggleUsername(
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
)).done(done).fail(fail).handleFloodErrors().send();
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
_api.request(MTPchannels_ToggleUsername(
|
||||
channel->inputChannel,
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
)).done(done).fail(fail).handleFloodErrors().send();
|
||||
} else if (const auto botUserInput = BotUserInput(peer)) {
|
||||
_api.request(MTPbots_ToggleUsername(
|
||||
*botUserInput,
|
||||
MTP_string(username),
|
||||
MTP_bool(active)
|
||||
)).done(done).fail(fail).send();
|
||||
)).done(done).fail(fail).handleFloodErrors().send();
|
||||
} else {
|
||||
return rpl::never<rpl::no_value, Error>();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ class Usernames final {
|
||||
public:
|
||||
enum class Error {
|
||||
TooMuch,
|
||||
Flood,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_music.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -106,10 +107,6 @@ using PhotoFileLocationId = Data::PhotoFileLocationId;
|
||||
using DocumentFileLocationId = Data::DocumentFileLocationId;
|
||||
using UpdatedFileReferences = Data::UpdatedFileReferences;
|
||||
|
||||
[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
|
||||
return TimeId(msgId >> 32);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> ShowForPeer(
|
||||
not_null<PeerData*> peer) {
|
||||
if (const auto window = Core::App().windowFor(peer)) {
|
||||
@@ -154,6 +151,14 @@ void ShowChannelsLimitBox(not_null<PeerData*> peer) {
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace Api {
|
||||
|
||||
TimeId UnixtimeFromMsgId(mtpMsgId msgId) {
|
||||
return TimeId(msgId >> 32);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
: MTP::Sender(&session->account().mtp())
|
||||
, _session(session)
|
||||
@@ -204,6 +209,27 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||
|
||||
ApiWrap::~ApiWrap() = default;
|
||||
|
||||
void ApiWrap::ProcessRecentSelfForwards(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPUpdates &updates,
|
||||
PeerId targetPeerId,
|
||||
PeerId fromPeerId) {
|
||||
auto newIds = MessageIdsList();
|
||||
updates.match([&](const MTPDupdates &data) {
|
||||
for (const auto &update : data.vupdates().v) {
|
||||
update.match([&](const MTPDupdateMessageID &d) {
|
||||
newIds.push_back(FullMsgId(targetPeerId, d.vid().v));
|
||||
}, [](const auto &) {});
|
||||
}
|
||||
}, [](const auto &) {});
|
||||
if (!newIds.empty()) {
|
||||
session->data().addRecentSelfForwards({
|
||||
.fromPeerId = fromPeerId,
|
||||
.ids = newIds,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Main::Session &ApiWrap::session() const {
|
||||
return *_session;
|
||||
}
|
||||
@@ -374,9 +400,9 @@ void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
||||
order,
|
||||
ranges::back_inserter(topics),
|
||||
input);
|
||||
request(MTPchannels_ReorderPinnedForumTopics(
|
||||
MTP_flags(MTPchannels_ReorderPinnedForumTopics::Flag::f_force),
|
||||
forum->channel()->inputChannel,
|
||||
request(MTPmessages_ReorderPinnedForumTopics(
|
||||
MTP_flags(MTPmessages_ReorderPinnedForumTopics::Flag::f_force),
|
||||
forum->peer()->input,
|
||||
MTP_vector(topics)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
@@ -1088,6 +1114,8 @@ void ApiWrap::requestWallPaper(
|
||||
void ApiWrap::requestFullPeer(not_null<PeerData*> peer) {
|
||||
if (_fullPeerRequests.contains(peer)) {
|
||||
return;
|
||||
} else if (!peer->isUser() && !peer->barSettings().has_value()) {
|
||||
requestPeerSettings(peer);
|
||||
}
|
||||
|
||||
const auto requestId = [&] {
|
||||
@@ -1886,7 +1914,7 @@ void ApiWrap::sendNotifySettingsUpdates() {
|
||||
for (const auto topic : base::take(_updateNotifyTopics)) {
|
||||
request(MTPaccount_UpdateNotifySettings(
|
||||
MTP_inputNotifyForumTopic(
|
||||
topic->channel()->input,
|
||||
topic->peer()->input,
|
||||
MTP_int(topic->rootId())),
|
||||
topic->notify().serialize()
|
||||
)).afterDelay(kSmallDelayMs).send();
|
||||
@@ -2179,7 +2207,7 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
monoforumPeerId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
Api::UnixtimeFromMsgId(response.outerMsgId));
|
||||
const auto cloudDraft = history->cloudDraft(
|
||||
topicRootId,
|
||||
monoforumPeerId);
|
||||
@@ -2200,7 +2228,7 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
monoforumPeerId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
Api::UnixtimeFromMsgId(response.outerMsgId));
|
||||
const auto cloudDraft = history->cloudDraft(
|
||||
topicRootId,
|
||||
monoforumPeerId);
|
||||
@@ -2458,7 +2486,17 @@ void ApiWrap::refreshFileReference(
|
||||
v::match(origin.data, [&](Data::FileOriginMessage data) {
|
||||
if (const auto item = _session->data().message(data)) {
|
||||
const auto media = item->media();
|
||||
const auto storyId = media ? media->storyId() : FullStoryId();
|
||||
const auto mediaStory = media ? media->storyId() : FullStoryId();
|
||||
const auto storyId = mediaStory
|
||||
? mediaStory
|
||||
: FullStoryId{
|
||||
(IsStoryMsgId(item->id)
|
||||
? item->history()->peer->id
|
||||
: PeerId()),
|
||||
(IsStoryMsgId(item->id)
|
||||
? StoryIdFromMsgId(item->id)
|
||||
: StoryId())
|
||||
};
|
||||
if (storyId) {
|
||||
request(MTPstories_GetStoriesByID(
|
||||
_session->data().peer(storyId.peer)->input,
|
||||
@@ -2469,6 +2507,17 @@ void ApiWrap::refreshFileReference(
|
||||
request(MTPmessages_GetScheduledMessages(
|
||||
item->history()->peer->input,
|
||||
MTP_vector<MTPint>(1, MTP_int(realId))));
|
||||
} else if (item->isSavedMusicItem()) {
|
||||
const auto user = item->history()->peer->asUser();
|
||||
const auto media = item->media();
|
||||
const auto document = media ? media->document() : nullptr;
|
||||
if (user && document) {
|
||||
request(MTPusers_GetSavedMusicByID(
|
||||
user->inputUser,
|
||||
MTP_vector<MTPInputDocument>(1, document->mtpInput())));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
} else if (item->isBusinessShortcut()) {
|
||||
const auto &shortcuts = _session->data().shortcutMessages();
|
||||
const auto realId = shortcuts.lookupId(item);
|
||||
@@ -3030,18 +3079,20 @@ void ApiWrap::requestMessageAfterDate(
|
||||
return &messages.vmessages().v;
|
||||
};
|
||||
const auto list = result.match([&](
|
||||
const MTPDmessages_messages &data) {
|
||||
const MTPDmessages_messages &data) {
|
||||
peer->processTopics(data.vtopics());
|
||||
return handleMessages(data);
|
||||
}, [&](const MTPDmessages_messagesSlice &data) {
|
||||
peer->processTopics(data.vtopics());
|
||||
return handleMessages(data);
|
||||
}, [&](const MTPDmessages_channelMessages &data) {
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
channel->ptsReceived(data.vpts().v);
|
||||
channel->processTopics(data.vtopics());
|
||||
} else {
|
||||
LOG(("API Error: received messages.channelMessages when "
|
||||
"no channel was passed! (ApiWrap::jumpToDate)"));
|
||||
}
|
||||
peer->processTopics(data.vtopics());
|
||||
return handleMessages(data);
|
||||
}, [&](const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: received messages.messagesNotModified! "
|
||||
@@ -3340,8 +3391,8 @@ void ApiWrap::finishForwarding(const SendAction &action) {
|
||||
return;
|
||||
}
|
||||
|
||||
forwardMessages(std::move(toForward), action);
|
||||
history->setForwardDraft(topicRootId, monoforumPeerId, {});
|
||||
forwardMessages(std::move(toForward), action);
|
||||
}
|
||||
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -3362,6 +3413,22 @@ void ApiWrap::forwardMessages(
|
||||
|
||||
auto &histories = _session->data().histories();
|
||||
|
||||
for (auto i = begin(draft.items); i != end(draft.items);) {
|
||||
const auto item = *i;
|
||||
if (item->isSavedMusicItem()) {
|
||||
SendExistingDocument(MessageToSend(action), item->media()->document());
|
||||
i = draft.items.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
if (draft.items.empty()) {
|
||||
if (successCallback) {
|
||||
successCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct SharedCallback {
|
||||
int requestsLeft = 0;
|
||||
FnMut<void()> callback;
|
||||
@@ -3477,6 +3544,13 @@ void ApiWrap::forwardMessages(
|
||||
shared->callback();
|
||||
}
|
||||
finish();
|
||||
if (peer->isSelf() && session().premium()) {
|
||||
ProcessRecentSelfForwards(
|
||||
_session,
|
||||
result,
|
||||
peer->id,
|
||||
forwardFrom->id);
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (idsCopy) {
|
||||
for (const auto &[randomId, itemId] : *idsCopy) {
|
||||
@@ -3841,7 +3915,9 @@ void ApiWrap::sendShortcutMessages(
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
void ApiWrap::sendMessage(
|
||||
MessageToSend &&message,
|
||||
std::optional<MsgId> localMessageId) {
|
||||
const auto history = message.action.history;
|
||||
const auto peer = history->peer;
|
||||
auto &textWithTags = message.textWithTags;
|
||||
@@ -3891,7 +3967,9 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
|
||||
auto newId = FullMsgId(
|
||||
peer->id,
|
||||
_session->data().nextLocalMessageId());
|
||||
localMessageId
|
||||
? std::exchange(localMessageId, std::nullopt).value()
|
||||
: _session->data().nextLocalMessageId());
|
||||
auto randomId = base::RandomValue<uint64>();
|
||||
|
||||
TextUtilities::Trim(sending);
|
||||
@@ -4017,7 +4095,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
history->finishSavingCloudDraft(
|
||||
draftTopicRootId,
|
||||
draftMonoforumPeerId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
Api::UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
};
|
||||
const auto fail = [=](
|
||||
@@ -4032,7 +4110,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
history->finishSavingCloudDraft(
|
||||
draftTopicRootId,
|
||||
draftMonoforumPeerId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
Api::UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
};
|
||||
const auto mtpShortcut = Data::ShortcutIdToMTP(
|
||||
@@ -4228,7 +4306,7 @@ void ApiWrap::sendInlineResult(
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
monoforumPeerId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
Api::UnixtimeFromMsgId(response.outerMsgId));
|
||||
if (done) {
|
||||
done(true);
|
||||
}
|
||||
@@ -4237,7 +4315,7 @@ void ApiWrap::sendInlineResult(
|
||||
history->finishSavingCloudDraft(
|
||||
topicRootId,
|
||||
monoforumPeerId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
Api::UnixtimeFromMsgId(response.outerMsgId));
|
||||
if (done) {
|
||||
done(false);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
class WallPaper;
|
||||
struct ResolvedForwardDraft;
|
||||
enum class DefaultNotify;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
enum class StickersType : uchar;
|
||||
class Forum;
|
||||
class ForumTopic;
|
||||
@@ -129,6 +129,8 @@ QString RequestKey(Types &&...values) {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] TimeId UnixtimeFromMsgId(mtpMsgId msgId);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
class ApiWrap final : public MTP::Sender {
|
||||
@@ -363,7 +365,9 @@ public:
|
||||
void sendShortcutMessages(
|
||||
not_null<PeerData*> peer,
|
||||
BusinessShortcutId id);
|
||||
void sendMessage(MessageToSend &&message);
|
||||
void sendMessage(
|
||||
MessageToSend &&message,
|
||||
std::optional<MsgId> localMessageId = std::nullopt);
|
||||
void sendBotStart(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<UserData*> bot,
|
||||
@@ -428,6 +432,12 @@ public:
|
||||
|
||||
static constexpr auto kJoinErrorDuration = 5 * crl::time(1000);
|
||||
|
||||
static void ProcessRecentSelfForwards(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPUpdates &updates,
|
||||
PeerId targetPeerId,
|
||||
PeerId fromPeerId);
|
||||
|
||||
private:
|
||||
struct MessageDataRequest {
|
||||
using Callbacks = std::vector<Fn<void()>>;
|
||||
|
||||
@@ -13,12 +13,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/update_checker.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
@@ -158,3 +166,151 @@ QString currentVersionText() {
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
void ArchiveHintBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool unarchiveOnNewMessage,
|
||||
Fn<void()> onUnarchive) {
|
||||
box->setNoContentMargin(true);
|
||||
|
||||
const auto content = box->verticalLayout().get();
|
||||
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto &icon = st::dialogsArchiveUserpic;
|
||||
const auto rect = Rect(icon.size() * 2);
|
||||
auto owned = object_ptr<Ui::RpWidget>(content);
|
||||
owned->resize(rect.size());
|
||||
owned->setNaturalWidth(rect.width());
|
||||
const auto widget = box->addRow(std::move(owned), style::al_top);
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(widget);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::activeButtonBg);
|
||||
p.drawEllipse(rect);
|
||||
icon.paintInCenter(p, rect);
|
||||
}, widget->lifetime());
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_archive_hint_title(),
|
||||
st::boxTitle),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto label = box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
(unarchiveOnNewMessage
|
||||
? tr::lng_archive_hint_about_unmuted
|
||||
: tr::lng_archive_hint_about)(
|
||||
lt_link,
|
||||
tr::lng_archive_hint_about_link(
|
||||
lt_emoji,
|
||||
rpl::single(
|
||||
Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
|
||||
Ui::Text::RichLangValue
|
||||
) | rpl::map([](TextWithEntities text) {
|
||||
return Ui::Text::Link(std::move(text), 1);
|
||||
}),
|
||||
Ui::Text::RichLangValue),
|
||||
st::channelEarnHistoryRecipientLabel));
|
||||
label->resizeToWidth(box->width()
|
||||
- rect::m::sum::h(st::boxRowPadding));
|
||||
label->setLink(
|
||||
1,
|
||||
std::make_shared<GenericClickHandler>([=](ClickContext context) {
|
||||
if (context.button == Qt::LeftButton) {
|
||||
onUnarchive();
|
||||
}
|
||||
}));
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto padding = QMargins(
|
||||
st::settingsButton.padding.left(),
|
||||
st::boxRowPadding.top(),
|
||||
st::boxRowPadding.right(),
|
||||
st::boxRowPadding.bottom());
|
||||
const auto addEntry = [&](
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> about,
|
||||
const style::icon &icon) {
|
||||
const auto top = content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
std::move(title),
|
||||
st::channelEarnSemiboldLabel),
|
||||
padding);
|
||||
Ui::AddSkip(content, st::channelEarnHistoryThreeSkip);
|
||||
content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
std::move(about),
|
||||
st::channelEarnHistoryRecipientLabel),
|
||||
padding);
|
||||
const auto left = Ui::CreateChild<Ui::RpWidget>(
|
||||
box->verticalLayout().get());
|
||||
left->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto p = Painter(left);
|
||||
icon.paint(p, 0, 0, left->width());
|
||||
}, left->lifetime());
|
||||
left->resize(icon.size());
|
||||
top->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &g) {
|
||||
left->moveToLeft(
|
||||
(g.left() - left->width()) / 2,
|
||||
g.top() + st::channelEarnHistoryThreeSkip);
|
||||
}, left->lifetime());
|
||||
};
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_1(),
|
||||
tr::lng_archive_hint_section_1_info(),
|
||||
st::menuIconArchive);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_2(),
|
||||
tr::lng_archive_hint_section_2_info(),
|
||||
st::menuIconStealth);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
addEntry(
|
||||
tr::lng_archive_hint_section_3(),
|
||||
tr::lng_archive_hint_section_3_info(),
|
||||
st::menuIconStoriesSavedSection);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
{
|
||||
const auto &st = st::premiumPreviewDoubledLimitsBox;
|
||||
box->setStyle(st);
|
||||
auto button = object_ptr<Ui::RoundButton>(
|
||||
box,
|
||||
tr::lng_archive_hint_button(),
|
||||
st::defaultActiveButton);
|
||||
button->setTextTransform(
|
||||
Ui::RoundButton::TextTransform::NoTransform);
|
||||
button->resizeToWidth(box->width()
|
||||
- st.buttonPadding.left()
|
||||
- st.buttonPadding.left());
|
||||
button->setClickedCallback([=] { box->closeBox(); });
|
||||
box->addButton(std::move(button));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/layers/generic_box.h"
|
||||
|
||||
void AboutBox(not_null<Ui::GenericBox*> box);
|
||||
void ArchiveHintBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
bool unarchiveOnNewMessage,
|
||||
Fn<void()> onUnarchive);
|
||||
|
||||
QString telegramFaqLink();
|
||||
QString currentVersionText();
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/file_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -18,7 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Ui {
|
||||
namespace {
|
||||
|
||||
constexpr auto kUrl = "https://promote.telegram.org"_cs;
|
||||
constexpr auto kUrl = "https://ads.telegram.org"_cs;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -54,8 +55,16 @@ void AboutSponsoredBox(not_null<Ui::GenericBox*> box) {
|
||||
};
|
||||
|
||||
const auto &stLabel = st::aboutLabel;
|
||||
const auto info1 = box->addRow(object_ptr<FlatLabel>(box, stLabel));
|
||||
info1->setText(tr::lng_sponsored_info_description1(tr::now));
|
||||
auto text1 = tr::lng_sponsored_info_description1_linked(
|
||||
lt_link,
|
||||
rpl::combine(
|
||||
tr::lng_sponsored_info_description1_link(),
|
||||
tr::lng_sponsored_info_description1_url()
|
||||
) | rpl::map([](const QString &text, const QString &url) {
|
||||
return Ui::Text::Link(text, url);
|
||||
}),
|
||||
Ui::Text::RichLangValue);
|
||||
box->addRow(object_ptr<FlatLabel>(box, std::move(text1), stLabel));
|
||||
|
||||
box->addSkip(st::sponsoredUrlButtonSkip);
|
||||
addUrl();
|
||||
|
||||
@@ -451,12 +451,12 @@ auto BackgroundBox::Inner::resolveResetCustomPaper() const
|
||||
return {};
|
||||
}
|
||||
const auto nonCustom = Window::Theme::Background()->paper();
|
||||
const auto themeEmoji = _forPeer->themeEmoji();
|
||||
if (forChannel() || themeEmoji.isEmpty()) {
|
||||
const auto themeToken = _forPeer->themeToken();
|
||||
if (forChannel() || themeToken.isEmpty()) {
|
||||
return nonCustom;
|
||||
}
|
||||
const auto &themes = _forPeer->owner().cloudThemes();
|
||||
const auto theme = themes.themeForEmoji(themeEmoji);
|
||||
const auto theme = themes.themeForToken(themeToken);
|
||||
if (!theme) {
|
||||
return nonCustom;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
return paper;
|
||||
}
|
||||
const auto &themes = session->data().cloudThemes();
|
||||
if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
|
||||
if (const auto theme = themes.themeForToken(paper.emojiId())) {
|
||||
using Type = Data::CloudThemeType;
|
||||
const auto type = dark ? Type::Dark : Type::Light;
|
||||
const auto i = theme->settings.find(type);
|
||||
|
||||
@@ -754,11 +754,17 @@ createPollFieldTitlePadding: margins(22px, 7px, 10px, 6px);
|
||||
|
||||
sendGifWithCaptionEmojiPosition: point(-30px, 23px);
|
||||
|
||||
notesFieldWithEmoji: InputField(defaultInputField) {
|
||||
textMargins: margins(0px, 28px, 30px, 4px);
|
||||
// border: 0px;
|
||||
// borderActive: 0px;
|
||||
}
|
||||
|
||||
backgroundCheckbox: Checkbox(defaultCheckbox) {
|
||||
textFg: msgServiceFg;
|
||||
textFgActive: msgServiceFg;
|
||||
|
||||
width: -50px;
|
||||
width: -10px;
|
||||
margin: margins(0px, 0px, 0px, 0px);
|
||||
|
||||
textPosition: point(0px, 6px);
|
||||
@@ -807,6 +813,7 @@ urlAuthCheckbox: Checkbox(defaultBoxCheckbox) {
|
||||
|
||||
addContactFieldMargin: margins(19px, 0px, 19px, 10px);
|
||||
addContactWarningMargin: margins(19px, 10px, 19px, 5px);
|
||||
editContactSuggestBirthday: icon{{ "settings/birthday_add-22x22", lightButtonFg }};
|
||||
blockUserConfirmation: FlatLabel(boxLabel) {
|
||||
minWidth: 240px;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -111,16 +112,13 @@ void DeleteMessagesBox::prepare() {
|
||||
Ui::Text::RichLangValue);
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
} else if (_wipeHistoryJustClear) {
|
||||
const auto isChannel = peer->isBroadcast();
|
||||
const auto isPublicGroup = peer->isMegagroup()
|
||||
&& peer->asChannel()->isPublic();
|
||||
if (isChannel || isPublicGroup) {
|
||||
canDelete = false;
|
||||
}
|
||||
const auto isChannel = peer->isChannel() && !peer->isMegagroup();
|
||||
_revokeJustClearForChannel = isChannel;
|
||||
details.text = isChannel
|
||||
? tr::lng_no_clear_history_channel(tr::now)
|
||||
: isPublicGroup
|
||||
? tr::lng_no_clear_history_group(tr::now)
|
||||
? tr::lng_sure_delete_channel_history(
|
||||
tr::now,
|
||||
lt_channel,
|
||||
peer->name())
|
||||
: peer->isSelf()
|
||||
? tr::lng_sure_delete_saved_messages(tr::now)
|
||||
: peer->isUser()
|
||||
@@ -156,7 +154,8 @@ void DeleteMessagesBox::prepare() {
|
||||
}
|
||||
deleteStyle = &st::attentionBoxButton;
|
||||
}
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
if (_revokeJustClearForChannel) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(
|
||||
this,
|
||||
revoke->checkbox,
|
||||
@@ -226,12 +225,14 @@ void DeleteMessagesBox::prepare() {
|
||||
search->searchMessages({ .from = _moderateFrom });
|
||||
}
|
||||
} else {
|
||||
details.text = (_ids.size() == 1)
|
||||
details.text = hasSavedMusicMessages()
|
||||
? tr::lng_selected_remove_saved_music(tr::now)
|
||||
: (_ids.size() == 1)
|
||||
? tr::lng_selected_delete_sure_this(tr::now)
|
||||
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
|
||||
if (const auto peer = checkFromSinglePeer()) {
|
||||
auto count = int(_ids.size());
|
||||
if (hasScheduledMessages()) {
|
||||
if (hasScheduledMessages() || hasSavedMusicMessages()) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
const auto &settings = Core::App().settings();
|
||||
const auto revokeByDefault
|
||||
@@ -285,6 +286,7 @@ void DeleteMessagesBox::prepare() {
|
||||
}
|
||||
}
|
||||
_text.create(this, rpl::single(std::move(details)), st::boxLabel);
|
||||
_text->resizeToWidth(st::boxWidth - rect::m::sum::h(st::boxPadding));
|
||||
|
||||
if (_wipeHistoryJustClear && _wipeHistoryPeer) {
|
||||
const auto validator = TTLMenu::TTLValidator(
|
||||
@@ -314,28 +316,36 @@ void DeleteMessagesBox::prepare() {
|
||||
addButton(tr::lng_about_done(), [=] { closeBox(); });
|
||||
}
|
||||
|
||||
auto fullHeight = st::boxPadding.top()
|
||||
+ _text->height()
|
||||
+ st::boxPadding.bottom();
|
||||
if (_moderateFrom) {
|
||||
fullHeight += st::boxMediumSkip;
|
||||
if (_banUser) {
|
||||
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
|
||||
const auto &padding = st::boxPadding;
|
||||
rpl::combine(
|
||||
widthValue(),
|
||||
_text->naturalWidthValue()
|
||||
) | rpl::start_with_next([=](int full, int) {
|
||||
_text->resizeToNaturalWidth(full - padding.left() - padding.right());
|
||||
|
||||
auto fullHeight = st::boxPadding.top()
|
||||
+ _text->height()
|
||||
+ st::boxPadding.bottom();
|
||||
if (_moderateFrom) {
|
||||
fullHeight += st::boxMediumSkip;
|
||||
if (_banUser) {
|
||||
fullHeight += _banUser->heightNoMargins() + st::boxLittleSkip;
|
||||
}
|
||||
fullHeight += _reportSpam->heightNoMargins();
|
||||
if (_deleteAll) {
|
||||
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
|
||||
}
|
||||
} else if (_revoke) {
|
||||
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
|
||||
}
|
||||
fullHeight += _reportSpam->heightNoMargins();
|
||||
if (_deleteAll) {
|
||||
fullHeight += st::boxLittleSkip + _deleteAll->heightNoMargins();
|
||||
if (_autoDeleteSettings) {
|
||||
fullHeight += st::boxMediumSkip
|
||||
+ _autoDeleteSettings->height()
|
||||
+ st::boxLittleSkip;
|
||||
}
|
||||
} else if (_revoke) {
|
||||
fullHeight += st::boxMediumSkip + _revoke->heightNoMargins();
|
||||
}
|
||||
if (_autoDeleteSettings) {
|
||||
fullHeight += st::boxMediumSkip
|
||||
+ _autoDeleteSettings->height()
|
||||
+ st::boxLittleSkip;
|
||||
}
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
_fullHeight = fullHeight;
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
_fullHeight = fullHeight;
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
@@ -349,6 +359,17 @@ bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasSavedMusicMessages() const {
|
||||
for (const auto &fullId : _ids) {
|
||||
if (const auto item = _session->data().message(fullId)) {
|
||||
if (item->isSavedMusicItem()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
|
||||
auto result = (PeerData*)nullptr;
|
||||
for (const auto &fullId : _ids) {
|
||||
@@ -554,12 +575,17 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
!_revoke->checked());
|
||||
Core::App().saveSettingsDelayed();
|
||||
}
|
||||
const auto revoke = _revoke ? _revoke->checked() : _revokeForBot;
|
||||
const auto revoke = _revoke
|
||||
? _revoke->checked()
|
||||
: (_revokeForBot || _revokeJustClearForChannel);
|
||||
const auto session = _session;
|
||||
const auto invokeCallbackAndClose = [&] {
|
||||
// deleteMessages can initiate closing of the current section,
|
||||
// which will cause this box to be destroyed.
|
||||
const auto weak = base::make_weak(this);
|
||||
if (hasSavedMusicMessages()) {
|
||||
uiShow()->showToast(tr::lng_saved_music_removed(tr::now));
|
||||
}
|
||||
if (const auto callback = _deleteConfirmedCallback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ private:
|
||||
void deleteAndClear();
|
||||
[[nodiscard]] PeerData *checkFromSinglePeer() const;
|
||||
[[nodiscard]] bool hasScheduledMessages() const;
|
||||
[[nodiscard]] bool hasSavedMusicMessages() const;
|
||||
[[nodiscard]] std::optional<RevokeConfig> revokeText(
|
||||
not_null<PeerData*> peer) const;
|
||||
[[nodiscard]] PaidPostType paidPostType() const;
|
||||
@@ -75,6 +76,7 @@ private:
|
||||
bool _moderateDeleteAll = false;
|
||||
|
||||
bool _revokeForBot = false;
|
||||
bool _revokeJustClearForChannel = false;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _text = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _revoke = { nullptr };
|
||||
|
||||
@@ -8,37 +8,42 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/edit_privacy_box.h"
|
||||
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "apiwrap.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h"
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "boxes/peers/edit_peer_invite_link.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_premium.h"
|
||||
#include "settings/settings_privacy_controllers.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "ui/boxes/peer_qr_box.h"
|
||||
#include "ui/controls/invite_link_buttons.h"
|
||||
#include "ui/controls/invite_link_label.h"
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_window.h"
|
||||
|
||||
namespace {
|
||||
@@ -473,15 +478,15 @@ auto PrivacyExceptionsBoxController::createRow(not_null<History*> history)
|
||||
const auto labels = raw->add(object_ptr<Ui::RpWidget>(raw));
|
||||
const auto min = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
QString::number(minValue),
|
||||
Lang::FormatCountDecimal(minValue),
|
||||
*labelStyle);
|
||||
const auto max = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
QString::number(maxValue),
|
||||
Lang::FormatCountDecimal(maxValue),
|
||||
*labelStyle);
|
||||
const auto current = Ui::CreateChild<Ui::FlatLabel>(
|
||||
raw,
|
||||
QString::number(value),
|
||||
Lang::FormatCountDecimal(value),
|
||||
*labelStyle);
|
||||
min->setTextColorOverride(st::windowSubTextFg->c);
|
||||
max->setTextColorOverride(st::windowSubTextFg->c);
|
||||
@@ -511,7 +516,7 @@ auto PrivacyExceptionsBoxController::createRow(not_null<History*> history)
|
||||
};
|
||||
const auto updateByValue = [=](int value) {
|
||||
current->setText(value > 0
|
||||
? tr::lng_action_gift_for_stars(tr::now, lt_count, value)
|
||||
? tr::lng_action_gift_for_stars(tr::now, lt_count_decimal, value)
|
||||
: tr::lng_manage_monoforum_free(tr::now));
|
||||
|
||||
state->index = 0;
|
||||
@@ -1293,7 +1298,7 @@ void EditDirectMessagesPriceBox(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
style::margins());
|
||||
wrap->toggle(savedValue.has_value(), anim::type::instant);
|
||||
wrap->toggleOn(toggle->toggledChanges());
|
||||
|
||||
@@ -1312,6 +1317,58 @@ void EditDirectMessagesPriceBox(
|
||||
*result = stars;
|
||||
}, box->lifetime());
|
||||
|
||||
if (const auto username = channel->username(); !username.isEmpty()) {
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSubsectionTitle(
|
||||
inner,
|
||||
tr::lng_manage_monoforum_link_subtitle());
|
||||
|
||||
constexpr auto kDirectParam = "?direct"_cs;
|
||||
const auto link = channel->session().createInternalLinkFull(username)
|
||||
+ kDirectParam.utf8();
|
||||
const auto copyLink = [=] {
|
||||
TextUtilities::SetClipboardText(TextForMimeData::Simple(link));
|
||||
box->uiShow()->showToast(tr::lng_group_invite_copied(tr::now));
|
||||
};
|
||||
const auto shareLink = [=] {
|
||||
box->uiShow()->showBox(ShareInviteLinkBox(channel, link));
|
||||
};
|
||||
const auto createMenu = [=] {
|
||||
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||
inner,
|
||||
st::popupMenuWithIcons);
|
||||
result->addAction(
|
||||
tr::lng_group_invite_context_qr(tr::now),
|
||||
[=] {
|
||||
box->uiShow()->showBox(Box([=](
|
||||
not_null<Ui::GenericBox*> qrBox) {
|
||||
Ui::FillPeerQrBox(qrBox, channel, link, nullptr);
|
||||
}));
|
||||
},
|
||||
&st::menuIconQrCode);
|
||||
return result;
|
||||
};
|
||||
|
||||
auto linkText = Ui::Text::StripUrlProtocol(link);
|
||||
const auto label = inner->lifetime().make_state<Ui::InviteLinkLabel>(
|
||||
inner,
|
||||
rpl::single(std::move(linkText)),
|
||||
createMenu);
|
||||
inner->add(
|
||||
label->take(),
|
||||
st::inviteLinkFieldPadding);
|
||||
|
||||
label->clicks() | rpl::start_with_next(copyLink, label->lifetime());
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
AddCopyShareLinkButtons(inner, copyLink, shareLink);
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
Ui::AddDivider(inner);
|
||||
}
|
||||
|
||||
box->addButton(tr::lng_settings_save(), [=] {
|
||||
const auto weak = base::make_weak(box);
|
||||
callback(toggle->toggled() ? *result : std::optional<int>());
|
||||
|
||||
@@ -143,6 +143,11 @@ private:
|
||||
int id,
|
||||
TextWithEntities text,
|
||||
anim::type animated);
|
||||
void insertTask(
|
||||
int beforeIndex,
|
||||
int id,
|
||||
TextWithEntities text,
|
||||
anim::type animated);
|
||||
void initTaskField(not_null<Task*> task, TextWithEntities text);
|
||||
void checkLastTask();
|
||||
void validateState();
|
||||
@@ -150,6 +155,9 @@ private:
|
||||
void destroy(std::unique_ptr<Task> task);
|
||||
void removeDestroyed(not_null<Task*> field);
|
||||
int findField(not_null<Ui::InputField*> field) const;
|
||||
void handlePaste(
|
||||
not_null<Ui::InputField*> field,
|
||||
const QStringList &list);
|
||||
|
||||
not_null<Ui::BoxContent*> _box;
|
||||
not_null<Ui::VerticalLayout*> _container;
|
||||
@@ -187,6 +195,27 @@ void InitField(
|
||||
options);
|
||||
}
|
||||
|
||||
[[nodiscard]] QStringList ParsePastedList(const QString &text) {
|
||||
auto list = QStringView(text).split('\n');
|
||||
for (auto i = list.begin(); i != list.end();) {
|
||||
auto text = i->trimmed();
|
||||
if (text.isEmpty() && (i + 1 != list.end())) {
|
||||
i = list.erase(i);
|
||||
} else {
|
||||
*i++ = text;
|
||||
}
|
||||
}
|
||||
if (list.size() < 2) {
|
||||
return {};
|
||||
}
|
||||
auto result = QStringList();
|
||||
result.reserve(list.size());
|
||||
for (const auto &view : list) {
|
||||
result.push_back(view.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<Ui::FlatLabel*> CreateWarningLabel(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Ui::InputField*> field,
|
||||
@@ -263,11 +292,14 @@ Tasks::Task::Task(
|
||||
session->user()->isPremium()
|
||||
? st::createPollOptionFieldPremium
|
||||
: st::createPollOptionField,
|
||||
Ui::InputField::Mode::NoNewlines,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_todo_create_list_add()))
|
||||
, _limit(session->appConfig().todoListItemTextLimit()) {
|
||||
InitField(outer, _field, session);
|
||||
_field->setMaxLength(_limit + kErrorLimit);
|
||||
|
||||
// Don't limit max length, because user can paste long list of items.
|
||||
//_field->setMaxLength(_limit + kErrorLimit);
|
||||
|
||||
_field->show();
|
||||
if (locked) {
|
||||
_field->setDisabled(true);
|
||||
@@ -629,24 +661,35 @@ void Tasks::addTask(
|
||||
int id,
|
||||
TextWithEntities text,
|
||||
anim::type animated) {
|
||||
insertTask(_list.size(), id, std::move(text), animated);
|
||||
}
|
||||
|
||||
void Tasks::insertTask(
|
||||
int beforeIndex,
|
||||
int id,
|
||||
TextWithEntities text,
|
||||
anim::type animated) {
|
||||
if (full()) {
|
||||
return;
|
||||
}
|
||||
Assert(beforeIndex >= 0 && beforeIndex <= _list.size());
|
||||
if (_list.size() > 1) {
|
||||
(*(_list.end() - 2))->removePlaceholder();
|
||||
(*(_list.end() - 2))->toggleRemoveAlways(true);
|
||||
}
|
||||
const auto locked = id && _existingLocked;
|
||||
_list.push_back(std::make_unique<Task>(
|
||||
_box,
|
||||
_container,
|
||||
&_controller->session(),
|
||||
id,
|
||||
_position + _list.size() + _destroyed.size(),
|
||||
locked));
|
||||
const auto field = _list.back()->field();
|
||||
const auto i = _list.insert(
|
||||
begin(_list) + beforeIndex,
|
||||
std::make_unique<Task>(
|
||||
_box,
|
||||
_container,
|
||||
&_controller->session(),
|
||||
id,
|
||||
_position + beforeIndex + _destroyed.size(),
|
||||
locked));
|
||||
const auto field = i->get()->field();
|
||||
if (!locked) {
|
||||
initTaskField(_list.back().get(), std::move(text));
|
||||
initTaskField(i->get(), std::move(text));
|
||||
} else {
|
||||
InitMessageFieldHandlers(
|
||||
_controller,
|
||||
@@ -659,7 +702,7 @@ void Tasks::addTask(
|
||||
});
|
||||
}
|
||||
field->finishAnimating();
|
||||
_list.back()->show(animated);
|
||||
i->get()->show(animated);
|
||||
fixShadows();
|
||||
}
|
||||
|
||||
@@ -706,6 +749,14 @@ void Tasks::initTaskField(not_null<Task*> task, TextWithEntities text) {
|
||||
}, field->lifetime());
|
||||
field->changes(
|
||||
) | rpl::start_with_next([=] {
|
||||
auto list = ParsePastedList(field->getLastText());
|
||||
if (!list.empty()) {
|
||||
field->setText(list.front());
|
||||
field->forceProcessContentsChanges();
|
||||
|
||||
list.pop_front();
|
||||
handlePaste(field, list);
|
||||
}
|
||||
Ui::PostponeCall(crl::guard(field, [=] {
|
||||
validateState();
|
||||
}));
|
||||
@@ -793,6 +844,27 @@ int Tasks::findField(not_null<Ui::InputField*> field) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Tasks::handlePaste(
|
||||
not_null<Ui::InputField*> field,
|
||||
const QStringList &list) {
|
||||
const auto index = findField(field);
|
||||
for (auto i = 0, count = int(list.size()); i != count; ++i) {
|
||||
insertTask(
|
||||
index + 1 + i,
|
||||
0, // id
|
||||
TextWithEntities{ list[i] },
|
||||
anim::type::instant);
|
||||
}
|
||||
const auto last = std::min(
|
||||
int(index + list.size()),
|
||||
int(_list.size()) - 1);
|
||||
const auto add = _list[last]->field();
|
||||
crl::on_main(add, [=] {
|
||||
add->setCursorPosition(add->getLastText().size());
|
||||
add->setFocus();
|
||||
});
|
||||
}
|
||||
|
||||
void Tasks::checkLastTask() {
|
||||
removeEmptyTail();
|
||||
addEmptyTask();
|
||||
|
||||
@@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/filter_icons.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/power_saving.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -593,7 +594,7 @@ void EditFilterBox(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
const auto h = st::normalFont->height;
|
||||
preview->setGeometry(
|
||||
colors->x(),
|
||||
rect::right(colors) - st::settingsFilterTagPreviewSkip,
|
||||
r.y() + (r.height() - h) / 2 + st::lineWidth,
|
||||
colors->width(),
|
||||
h);
|
||||
|
||||
@@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/label_with_custom_emoji.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_channel_earn.h"
|
||||
@@ -53,9 +52,8 @@ void GiftCreditsBox(
|
||||
Ui::AddSkip(content);
|
||||
const auto &stUser = st::premiumGiftsUserpicButton;
|
||||
const auto userpicWrap = content->add(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
object_ptr<Ui::UserpicButton>(content, peer, stUser)));
|
||||
object_ptr<Ui::UserpicButton>(content, peer, stUser),
|
||||
style::al_top);
|
||||
userpicWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -78,7 +76,7 @@ void GiftCreditsBox(
|
||||
u"internal:stars_examples"_q);
|
||||
});
|
||||
content->add(
|
||||
Ui::CreateLabelWithCustomEmoji(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_credits_box_history_entry_gift_out_about(
|
||||
lt_user,
|
||||
@@ -86,7 +84,6 @@ void GiftCreditsBox(
|
||||
lt_link,
|
||||
std::move(link),
|
||||
Ui::Text::RichLangValue),
|
||||
Core::TextContext({ .session = &peer->session() }),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
@@ -100,6 +97,7 @@ void GiftCreditsBox(
|
||||
peer,
|
||||
CreditsAmount(),
|
||||
[=] { gifted(); box->uiShow()->hideLayer(); },
|
||||
box->showFinishes(),
|
||||
tr::lng_credits_summary_options_subtitle(),
|
||||
{});
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ struct CreditsHistoryEntry;
|
||||
struct GiveawayStart;
|
||||
struct GiveawayResults;
|
||||
struct SubscriptionEntry;
|
||||
struct UniqueGift;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
@@ -77,7 +78,12 @@ void AddStarGiftTable(
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::CreditsHistoryEntry &entry,
|
||||
Fn<void()> convertToStars,
|
||||
Fn<void()> startUpgrade);
|
||||
Fn<void()> startUpgrade,
|
||||
Fn<void(Fn<void()> removed)> removeDetails);
|
||||
void AddTransferGiftTable(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
std::shared_ptr<Data::UniqueGift> unique);
|
||||
void AddCreditsHistoryEntryTable(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
@@ -106,3 +112,9 @@ void AddChannelEarnTable(
|
||||
std::shared_ptr<Ui::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
const Data::CreditsHistoryEntry &entry);
|
||||
|
||||
void AddUniqueGiftValueTable(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
Settings::CreditsEntryBoxStyleOverrides st,
|
||||
const Data::CreditsHistoryEntry &entry);
|
||||
|
||||
@@ -138,6 +138,8 @@ void CreateModerateMessagesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
const HistoryItemsList &items,
|
||||
Fn<void()> confirmed) {
|
||||
Expects(!items.empty());
|
||||
|
||||
using Controller = Ui::ExpandablePeerListController;
|
||||
|
||||
const auto [allCanBan, allCanDelete, participants]
|
||||
@@ -158,8 +160,12 @@ void CreateModerateMessagesBox(
|
||||
participants.size()).width(),
|
||||
0);
|
||||
|
||||
const auto session = &items.front()->history()->session();
|
||||
const auto historyPeerId = items.front()->history()->peer->id;
|
||||
const auto itemsCount = int(items.size());
|
||||
const auto firstItem = items.front();
|
||||
const auto history = firstItem->history();
|
||||
const auto session = &history->session();
|
||||
const auto historyPeerId = history->peer->id;
|
||||
const auto ids = session->data().itemsToIds(items);
|
||||
|
||||
using Request = Fn<void(not_null<PeerData*>, not_null<ChannelData*>)>;
|
||||
const auto sequentiallyRequest = [=](
|
||||
@@ -242,11 +248,11 @@ void CreateModerateMessagesBox(
|
||||
const auto title = box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
(items.size() == 1)
|
||||
(itemsCount == 1)
|
||||
? tr::lng_selected_delete_sure_this()
|
||||
: tr::lng_selected_delete_sure(
|
||||
lt_count,
|
||||
rpl::single(items.size()) | tr::to_count()),
|
||||
rpl::single(itemsCount) | tr::to_count()),
|
||||
st::boxLabel));
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSkip(inner);
|
||||
@@ -264,7 +270,6 @@ void CreateModerateMessagesBox(
|
||||
Ui::AddExpandablePeerList(report, controller, inner);
|
||||
handleSubmition(report);
|
||||
|
||||
const auto ids = items.front()->from()->owner().itemsToIds(items);
|
||||
handleConfirmation(report, controller, [=](
|
||||
not_null<PeerData*> p,
|
||||
not_null<ChannelData*> c) {
|
||||
@@ -296,12 +301,11 @@ void CreateModerateMessagesBox(
|
||||
: tr::lng_delete_all_from_user(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(items.front()->from()->name()),
|
||||
Ui::Text::Bold(firstItem->from()->name()),
|
||||
Ui::Text::WithEntities),
|
||||
false,
|
||||
st::defaultBoxCheckbox),
|
||||
st::boxRowPadding + buttonPadding);
|
||||
const auto history = items.front()->history();
|
||||
auto messagesCounts = MessagesCountValue(history, participants);
|
||||
|
||||
const auto controller = box->lifetime().make_state<Controller>(
|
||||
@@ -311,6 +315,10 @@ void CreateModerateMessagesBox(
|
||||
});
|
||||
Ui::AddExpandablePeerList(deleteAll, controller, inner);
|
||||
{
|
||||
auto itemFromIds = items | ranges::views::transform([](
|
||||
const auto &item) {
|
||||
return item->from()->id;
|
||||
}) | ranges::to_vector;
|
||||
tr::lng_selected_delete_sure(
|
||||
lt_count,
|
||||
rpl::combine(
|
||||
@@ -320,7 +328,7 @@ void CreateModerateMessagesBox(
|
||||
: rpl::merge(
|
||||
controller->toggleRequestsFromInner.events(),
|
||||
controller->checkAllRequests.events())
|
||||
) | rpl::map([=, s = items.size()](const auto &map, bool c) {
|
||||
) | rpl::map([=](const auto &map, bool c) {
|
||||
const auto checked = (isSingle && !c)
|
||||
? Participants()
|
||||
: controller->collectRequests
|
||||
@@ -335,9 +343,9 @@ void CreateModerateMessagesBox(
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &item : items) {
|
||||
for (const auto &fromId : itemFromIds) {
|
||||
for (const auto &peer : checked) {
|
||||
if (peer->id == item->from()->id) {
|
||||
if (peer->id == fromId) {
|
||||
result--;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -728,8 +728,8 @@ QString PeerListRow::generateShortName() {
|
||||
}
|
||||
|
||||
Ui::PeerUserpicView &PeerListRow::ensureUserpicView() {
|
||||
if (!_userpic.cloud && peer()->userpicPaintingPeer()->hasUserpic()) {
|
||||
_userpic = peer()->userpicPaintingPeer()->createUserpicView();
|
||||
if (!_userpic.cloud && peer()->hasUserpic()) {
|
||||
_userpic = peer()->createUserpicView();
|
||||
}
|
||||
return _userpic;
|
||||
}
|
||||
@@ -738,9 +738,9 @@ PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback(
|
||||
bool forceRound) {
|
||||
const auto saved = !_savedMessagesStatus.isEmpty();
|
||||
const auto replies = _isRepliesMessagesChat;
|
||||
const auto peer = this->peer()->userpicPaintingPeer();
|
||||
const auto peer = this->peer();
|
||||
auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView();
|
||||
if (forceRound && peer->isForum()) {
|
||||
if (forceRound && (peer->isForum() || peer->isMonoforum())) {
|
||||
return ForceRoundUserpicCallback(peer);
|
||||
}
|
||||
return [=](Painter &p, int x, int y, int outerWidth, int size) mutable {
|
||||
@@ -1460,7 +1460,8 @@ void PeerListContent::setSearchMode(PeerListSearchMode mode) {
|
||||
_loadingAnimation = Ui::CreateLoadingPeerListItemWidget(
|
||||
this,
|
||||
_st.item,
|
||||
2);
|
||||
2,
|
||||
_controller->computeListSt().bg->c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -981,9 +981,9 @@ void ChooseTopicSearchController::searchQuery(const QString &query) {
|
||||
}
|
||||
|
||||
void ChooseTopicSearchController::searchOnServer() {
|
||||
_requestId = _api.request(MTPchannels_GetForumTopics(
|
||||
MTP_flags(MTPchannels_GetForumTopics::Flag::f_q),
|
||||
_forum->channel()->inputChannel,
|
||||
_requestId = _api.request(MTPmessages_GetForumTopics(
|
||||
MTP_flags(MTPmessages_GetForumTopics::Flag::f_q),
|
||||
_forum->peer()->input,
|
||||
MTP_string(_query),
|
||||
MTP_int(_offsetDate),
|
||||
MTP_int(_offsetId),
|
||||
|
||||
@@ -513,7 +513,7 @@ void InviteForbiddenController::setComplexCover() {
|
||||
container->add(
|
||||
MakeShowOrLabel(container, tr::lng_invite_upgrade_or()),
|
||||
st::inviteForbiddenOrLabelPadding,
|
||||
style::al_top);
|
||||
style::al_justify);
|
||||
}
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
|
||||
@@ -7,28 +7,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/peers/edit_contact_box.h"
|
||||
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_session.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "boxes/peers/edit_peer_common.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "chat_helpers/tabbed_panel.h"
|
||||
#include "chat_helpers/tabbed_selector.h"
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_premium_limits.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "editor/photo_editor_common.h"
|
||||
#include "editor/photo_editor_layer_widget.h"
|
||||
#include "history/view/controls/history_view_characters_limit.h"
|
||||
#include "info/profile/info_profile_cover.h"
|
||||
#include "info/userpic/info_userpic_emoji_builder_common.h"
|
||||
#include "info/userpic/info_userpic_emoji_builder_menu_item.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lottie/lottie_common.h"
|
||||
#include "lottie/lottie_frame_generator.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/animated_icon.h"
|
||||
#include "ui/controls/emoji_button_factory.h"
|
||||
#include "ui/controls/emoji_button.h"
|
||||
#include "ui/controls/userpic_button.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "ui/text/text_entity.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/fields/input_field.h"
|
||||
#include "ui/text/format_values.h" // Ui::FormatPhone
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "info/profile/info_profile_cover.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/painter.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_peer_photo.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kAnimationStartFrame = 0;
|
||||
constexpr auto kAnimationEndFrame = 21;
|
||||
|
||||
QString UserPhone(not_null<UserData*> user) {
|
||||
const auto phone = user->phone();
|
||||
return phone.isEmpty()
|
||||
@@ -43,17 +85,22 @@ void SendRequest(
|
||||
const QString &first,
|
||||
const QString &last,
|
||||
const QString &phone,
|
||||
const TextWithEntities ¬e,
|
||||
Fn<void()> done) {
|
||||
const auto wasContact = user->isContact();
|
||||
using Flag = MTPcontacts_AddContact::Flag;
|
||||
user->session().api().request(MTPcontacts_AddContact(
|
||||
MTP_flags(sharePhone
|
||||
? Flag::f_add_phone_privacy_exception
|
||||
: Flag(0)),
|
||||
MTP_flags(Flag::f_note
|
||||
| (sharePhone ? Flag::f_add_phone_privacy_exception : Flag(0))),
|
||||
user->inputUser,
|
||||
MTP_string(first),
|
||||
MTP_string(last),
|
||||
MTP_string(phone)
|
||||
MTP_string(phone),
|
||||
note.text.isEmpty()
|
||||
? MTPTextWithEntities()
|
||||
: MTP_textWithEntities(
|
||||
MTP_string(note.text),
|
||||
Api::EntitiesToMTP(&user->session(), note.entities))
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
user->setName(
|
||||
first,
|
||||
@@ -83,7 +130,8 @@ public:
|
||||
Controller(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<UserData*> user);
|
||||
not_null<UserData*> user,
|
||||
bool focusOnNotes = false);
|
||||
|
||||
void prepare();
|
||||
|
||||
@@ -91,17 +139,40 @@ private:
|
||||
void setupContent();
|
||||
void setupCover();
|
||||
void setupNameFields();
|
||||
void setupNotesField();
|
||||
void setupPhotoButtons();
|
||||
void setupDeleteContactButton();
|
||||
void setupWarning();
|
||||
void setupSharePhoneNumber();
|
||||
void initNameFields(
|
||||
not_null<Ui::InputField*> first,
|
||||
not_null<Ui::InputField*> last,
|
||||
bool inverted);
|
||||
void showPhotoMenu(bool suggest);
|
||||
void choosePhotoFile(bool suggest);
|
||||
void processChosenPhoto(QImage &&image, bool suggest);
|
||||
void processChosenPhotoWithMarkup(
|
||||
UserpicBuilder::Result &&data,
|
||||
bool suggest);
|
||||
void executeWithDelay(
|
||||
Fn<void()> callback,
|
||||
bool suggest,
|
||||
bool startAnimation = true);
|
||||
void finishIconAnimation(bool suggest);
|
||||
|
||||
not_null<Ui::GenericBox*> _box;
|
||||
not_null<Window::SessionController*> _window;
|
||||
not_null<UserData*> _user;
|
||||
bool _focusOnNotes = false;
|
||||
Ui::Checkbox *_sharePhone = nullptr;
|
||||
Ui::InputField *_notesField = nullptr;
|
||||
Ui::InputField *_firstNameField = nullptr;
|
||||
base::unique_qptr<ChatHelpers::TabbedPanel> _emojiPanel;
|
||||
base::unique_qptr<Ui::PopupMenu> _photoMenu;
|
||||
std::unique_ptr<Ui::AnimatedIcon> _suggestIcon;
|
||||
std::unique_ptr<Ui::AnimatedIcon> _cameraIcon;
|
||||
Ui::RpWidget *_suggestIconWidget = nullptr;
|
||||
Ui::RpWidget *_cameraIconWidget = nullptr;
|
||||
QString _phone;
|
||||
Fn<void()> _focus;
|
||||
Fn<void()> _save;
|
||||
@@ -112,10 +183,12 @@ private:
|
||||
Controller::Controller(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<UserData*> user)
|
||||
not_null<UserData*> user,
|
||||
bool focusOnNotes)
|
||||
: _box(box)
|
||||
, _window(window)
|
||||
, _user(user)
|
||||
, _focusOnNotes(focusOnNotes)
|
||||
, _phone(UserPhone(user)) {
|
||||
}
|
||||
|
||||
@@ -134,6 +207,9 @@ void Controller::prepare() {
|
||||
void Controller::setupContent() {
|
||||
setupCover();
|
||||
setupNameFields();
|
||||
setupNotesField();
|
||||
setupPhotoButtons();
|
||||
setupDeleteContactButton();
|
||||
setupWarning();
|
||||
setupSharePhoneNumber();
|
||||
}
|
||||
@@ -154,13 +230,14 @@ void Controller::setupCover() {
|
||||
|
||||
void Controller::setupNameFields() {
|
||||
const auto inverted = langFirstNameGoesSecond();
|
||||
const auto first = _box->addRow(
|
||||
_firstNameField = _box->addRow(
|
||||
object_ptr<Ui::InputField>(
|
||||
_box,
|
||||
st::defaultInputField,
|
||||
tr::lng_signup_firstname(),
|
||||
_user->firstName),
|
||||
st::addContactFieldMargin);
|
||||
const auto first = _firstNameField;
|
||||
auto preparedLast = object_ptr<Ui::InputField>(
|
||||
_box,
|
||||
st::defaultInputField,
|
||||
@@ -188,6 +265,11 @@ void Controller::initNameFields(
|
||||
_box->setTabOrder(last, first);
|
||||
}
|
||||
_focus = [=] {
|
||||
if (_focusOnNotes && _notesField) {
|
||||
_notesField->setFocusFast();
|
||||
_notesField->setCursorPosition(_notesField->getLastText().size());
|
||||
return;
|
||||
}
|
||||
const auto firstValue = getValue(first);
|
||||
const auto lastValue = getValue(last);
|
||||
const auto empty = firstValue.isEmpty() && lastValue.isEmpty();
|
||||
@@ -203,6 +285,22 @@ void Controller::initNameFields(
|
||||
(inverted ? last : first)->showError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_notesField) {
|
||||
const auto limit = Data::PremiumLimits(
|
||||
&_user->session()).contactNoteLengthCurrent();
|
||||
const auto remove = Ui::ComputeFieldCharacterCount(_notesField)
|
||||
- limit;
|
||||
if (remove > 0) {
|
||||
_box->showToast(tr::lng_contact_notes_limit_reached(
|
||||
tr::now,
|
||||
lt_count,
|
||||
remove));
|
||||
_notesField->setFocus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto user = _user;
|
||||
const auto personal = _updatedPersonalPhoto
|
||||
? _updatedPersonalPhoto()
|
||||
@@ -218,6 +316,16 @@ void Controller::initNameFields(
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto noteValue = _notesField
|
||||
? [&] {
|
||||
auto textWithTags = _notesField->getTextWithAppliedMarkdown();
|
||||
return TextWithEntities{
|
||||
base::take(textWithTags.text),
|
||||
TextUtilities::ConvertTextTagsToEntities(
|
||||
base::take(textWithTags.tags)),
|
||||
};
|
||||
}()
|
||||
: TextWithEntities();
|
||||
SendRequest(
|
||||
base::make_weak(_box),
|
||||
user,
|
||||
@@ -225,6 +333,7 @@ void Controller::initNameFields(
|
||||
firstValue,
|
||||
lastValue,
|
||||
_phone,
|
||||
noteValue,
|
||||
done);
|
||||
};
|
||||
const auto submit = [=] {
|
||||
@@ -257,6 +366,334 @@ void Controller::setupWarning() {
|
||||
st::addContactWarningMargin);
|
||||
}
|
||||
|
||||
void Controller::setupNotesField() {
|
||||
Ui::AddSkip(_box->verticalLayout());
|
||||
Ui::AddDivider(_box->verticalLayout());
|
||||
Ui::AddSkip(_box->verticalLayout());
|
||||
_notesField = _box->addRow(
|
||||
object_ptr<Ui::InputField>(
|
||||
_box,
|
||||
st::notesFieldWithEmoji,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_contact_add_notes(),
|
||||
QString()),
|
||||
st::addContactFieldMargin);
|
||||
_notesField->setMarkdownSet(Ui::MarkdownSet::Notes);
|
||||
_notesField->setCustomTextContext(Core::TextContext({
|
||||
.session = &_user->session()
|
||||
}));
|
||||
_notesField->setTextWithTags({
|
||||
_user->note().text,
|
||||
TextUtilities::ConvertEntitiesToTextTags(_user->note().entities)
|
||||
});
|
||||
|
||||
_notesField->setMarkdownReplacesEnabled(rpl::single(
|
||||
Ui::MarkdownEnabledState{
|
||||
Ui::MarkdownEnabled{
|
||||
{
|
||||
Ui::InputField::kTagBold,
|
||||
Ui::InputField::kTagItalic,
|
||||
Ui::InputField::kTagUnderline,
|
||||
Ui::InputField::kTagStrikeOut,
|
||||
Ui::InputField::kTagSpoiler
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const auto container = _box->getDelegate()->outerContainer();
|
||||
using Selector = ChatHelpers::TabbedSelector;
|
||||
_emojiPanel = base::make_unique_q<ChatHelpers::TabbedPanel>(
|
||||
container,
|
||||
_window,
|
||||
object_ptr<Selector>(
|
||||
nullptr,
|
||||
_window->uiShow(),
|
||||
Window::GifPauseReason::Layer,
|
||||
Selector::Mode::EmojiOnly));
|
||||
_emojiPanel->setDesiredHeightValues(
|
||||
1.,
|
||||
st::emojiPanMinHeight / 2,
|
||||
st::emojiPanMinHeight);
|
||||
_emojiPanel->hide();
|
||||
_emojiPanel->selector()->setCurrentPeer(_window->session().user());
|
||||
_emojiPanel->selector()->emojiChosen(
|
||||
) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) {
|
||||
Ui::InsertEmojiAtCursor(_notesField->textCursor(), data.emoji);
|
||||
}, _notesField->lifetime());
|
||||
_emojiPanel->selector()->customEmojiChosen(
|
||||
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
|
||||
const auto info = data.document->sticker();
|
||||
if (info
|
||||
&& info->setType == Data::StickersType::Emoji
|
||||
&& !_window->session().premium()) {
|
||||
ShowPremiumPreviewBox(
|
||||
_window,
|
||||
PremiumFeature::AnimatedEmoji);
|
||||
} else {
|
||||
Data::InsertCustomEmoji(_notesField, data.document);
|
||||
}
|
||||
}, _notesField->lifetime());
|
||||
|
||||
const auto emojiButton = Ui::AddEmojiToggleToField(
|
||||
_notesField,
|
||||
_box,
|
||||
_window,
|
||||
_emojiPanel.get(),
|
||||
st::sendGifWithCaptionEmojiPosition);
|
||||
emojiButton->show();
|
||||
|
||||
using Limit = HistoryView::Controls::CharactersLimitLabel;
|
||||
struct LimitState {
|
||||
base::unique_qptr<Limit> charsLimitation;
|
||||
};
|
||||
const auto limitState = _notesField->lifetime().make_state<LimitState>();
|
||||
|
||||
const auto checkCharsLimitation = [=] {
|
||||
const auto limit = Data::PremiumLimits(
|
||||
&_user->session()).contactNoteLengthCurrent();
|
||||
const auto remove = Ui::ComputeFieldCharacterCount(_notesField)
|
||||
- limit;
|
||||
if (!limitState->charsLimitation) {
|
||||
limitState->charsLimitation = base::make_unique_q<Limit>(
|
||||
_box->verticalLayout(),
|
||||
emojiButton,
|
||||
style::al_top,
|
||||
QMargins{ 0, -st::lineWidth, 0, 0 });
|
||||
_notesField->heightValue(
|
||||
) | rpl::start_with_next([=](int height) {
|
||||
const auto &st = _notesField->st();
|
||||
const auto hasMultipleLines = height >
|
||||
(st.textMargins.top()
|
||||
+ st.style.font->height
|
||||
+ st.textMargins.bottom() * 2);
|
||||
limitState->charsLimitation->setVisible(hasMultipleLines);
|
||||
limitState->charsLimitation->raise();
|
||||
}, limitState->charsLimitation->lifetime());
|
||||
}
|
||||
limitState->charsLimitation->setLeft(remove);
|
||||
};
|
||||
|
||||
_notesField->changes() | rpl::start_with_next([=] {
|
||||
checkCharsLimitation();
|
||||
}, _notesField->lifetime());
|
||||
|
||||
Ui::AddDividerText(
|
||||
_box->verticalLayout(),
|
||||
tr::lng_contact_add_notes_about());
|
||||
}
|
||||
|
||||
void Controller::setupPhotoButtons() {
|
||||
const auto iconSize = st::restoreUserpicIcon.size;
|
||||
auto nameValue = _firstNameField
|
||||
? rpl::merge(
|
||||
rpl::single(_firstNameField->getLastText().trimmed()),
|
||||
_firstNameField->changes() | rpl::map([=] {
|
||||
return _firstNameField->getLastText().trimmed();
|
||||
})) | rpl::map([=](const QString &text) {
|
||||
return text.isEmpty() ? Ui::kQEllipsis : text;
|
||||
})
|
||||
: rpl::single(_user->shortName()) | rpl::type_erased();
|
||||
const auto inner = _box->verticalLayout();
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
const auto suggestBirthdayWrap = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
inner,
|
||||
object_ptr<Ui::VerticalLayout>(inner)));
|
||||
|
||||
const auto suggestBirthdayButton = Settings::AddButtonWithIcon(
|
||||
suggestBirthdayWrap->entity(),
|
||||
tr::lng_suggest_birthday(),
|
||||
st::settingsButtonLight,
|
||||
{ &st::editContactSuggestBirthday });
|
||||
suggestBirthdayButton->setClickedCallback([=] {
|
||||
Core::App().openInternalUrl(
|
||||
u"internal:edit_birthday:suggest:%1"_q.arg(
|
||||
peerToUser(_user->id).bare),
|
||||
QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = base::make_weak(_window),
|
||||
}));
|
||||
});
|
||||
suggestBirthdayWrap->toggleOn(rpl::single(!_user->birthday().valid()));
|
||||
|
||||
_suggestIcon = Ui::MakeAnimatedIcon({
|
||||
.generator = [] {
|
||||
return std::make_unique<Lottie::FrameGenerator>(
|
||||
Lottie::ReadContent(
|
||||
QByteArray(),
|
||||
u":/animations/photo_suggest_icon.tgs"_q));
|
||||
},
|
||||
.sizeOverride = iconSize * style::DevicePixelRatio(),
|
||||
.colorized = true,
|
||||
});
|
||||
|
||||
_cameraIcon = Ui::MakeAnimatedIcon({
|
||||
.generator = [] {
|
||||
return std::make_unique<Lottie::FrameGenerator>(
|
||||
Lottie::ReadContent(
|
||||
QByteArray(),
|
||||
u":/animations/camera_outline.tgs"_q));
|
||||
},
|
||||
.sizeOverride = iconSize * style::DevicePixelRatio(),
|
||||
.colorized = true,
|
||||
});
|
||||
|
||||
const auto suggestButton = Settings::AddButtonWithIcon(
|
||||
inner,
|
||||
tr::lng_suggest_photo_for(lt_user, rpl::duplicate(nameValue)),
|
||||
st::settingsButtonLight,
|
||||
{ nullptr });
|
||||
|
||||
_suggestIconWidget = Ui::CreateChild<Ui::RpWidget>(suggestButton);
|
||||
_suggestIconWidget->resize(iconSize * style::DevicePixelRatio());
|
||||
_suggestIconWidget->paintRequest() | rpl::start_with_next([=] {
|
||||
if (_suggestIcon && _suggestIcon->valid()) {
|
||||
auto p = QPainter(_suggestIconWidget);
|
||||
const auto frame = _suggestIcon->frame(st::lightButtonFg->c);
|
||||
const auto rect = _suggestIconWidget->rect();
|
||||
p.drawImage(rect, frame);
|
||||
}
|
||||
}, _suggestIconWidget->lifetime());
|
||||
|
||||
suggestButton->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
_suggestIconWidget->move(
|
||||
st::settingsButtonLight.iconLeft - iconSize.width() / 2,
|
||||
(size.height() - _suggestIconWidget->height()) / 2);
|
||||
}, _suggestIconWidget->lifetime());
|
||||
|
||||
suggestButton->setClickedCallback([=] {
|
||||
if (_suggestIcon && _suggestIcon->valid()) {
|
||||
_suggestIcon->setCustomStartFrame(kAnimationStartFrame);
|
||||
_suggestIcon->setCustomEndFrame(kAnimationEndFrame);
|
||||
_suggestIcon->jumpToStart([=] { _suggestIconWidget->update(); });
|
||||
_suggestIcon->animate([=] { _suggestIconWidget->update(); });
|
||||
}
|
||||
showPhotoMenu(true);
|
||||
});
|
||||
|
||||
const auto setButton = Settings::AddButtonWithIcon(
|
||||
inner,
|
||||
tr::lng_set_photo_for_user(lt_user, rpl::duplicate(nameValue)),
|
||||
st::settingsButtonLight,
|
||||
{ nullptr });
|
||||
|
||||
_cameraIconWidget = Ui::CreateChild<Ui::RpWidget>(setButton);
|
||||
_cameraIconWidget->resize(iconSize * style::DevicePixelRatio());
|
||||
_cameraIconWidget->paintRequest() | rpl::start_with_next([=] {
|
||||
if (_cameraIcon && _cameraIcon->valid()) {
|
||||
auto p = QPainter(_cameraIconWidget);
|
||||
const auto frame = _cameraIcon->frame(st::lightButtonFg->c);
|
||||
const auto rect = _cameraIconWidget->rect();
|
||||
p.drawImage(rect, frame);
|
||||
}
|
||||
}, _cameraIconWidget->lifetime());
|
||||
|
||||
setButton->sizeValue() | rpl::start_with_next([=](QSize size) {
|
||||
_cameraIconWidget->move(
|
||||
st::settingsButtonLight.iconLeft - iconSize.width() / 2,
|
||||
(size.height() - _cameraIconWidget->height()) / 2);
|
||||
}, _cameraIconWidget->lifetime());
|
||||
|
||||
setButton->setClickedCallback([=] {
|
||||
if (_cameraIcon && _cameraIcon->valid()) {
|
||||
_cameraIcon->setCustomStartFrame(kAnimationStartFrame);
|
||||
_cameraIcon->setCustomEndFrame(kAnimationEndFrame);
|
||||
_cameraIcon->jumpToStart([=] { _cameraIconWidget->update(); });
|
||||
_cameraIcon->animate([=] { _cameraIconWidget->update(); });
|
||||
}
|
||||
showPhotoMenu(false);
|
||||
});
|
||||
|
||||
const auto resetButtonWrap = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
inner,
|
||||
object_ptr<Ui::VerticalLayout>(inner)));
|
||||
|
||||
const auto resetButton = Settings::AddButtonWithIcon(
|
||||
resetButtonWrap->entity(),
|
||||
tr::lng_profile_photo_reset(),
|
||||
st::settingsButtonLight,
|
||||
{ nullptr });
|
||||
|
||||
const auto userpicButton = Ui::CreateChild<Ui::UserpicButton>(
|
||||
resetButton,
|
||||
_window,
|
||||
_user,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
Ui::UserpicButton::Source::NonPersonalIfHasPersonal,
|
||||
st::restoreUserpicIcon);
|
||||
userpicButton->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
resetButton->sizeValue(
|
||||
) | rpl::start_with_next([=](QSize size) {
|
||||
userpicButton->move(
|
||||
st::settingsButtonLight.iconLeft,
|
||||
(size.height() - userpicButton->height()) / 2);
|
||||
}, userpicButton->lifetime());
|
||||
resetButtonWrap->toggleOn(
|
||||
_user->session().changes().peerFlagsValue(
|
||||
_user,
|
||||
Data::PeerUpdate::Flag::FullInfo
|
||||
) | rpl::map([=] {
|
||||
return _user->hasPersonalPhoto();
|
||||
}) | rpl::distinct_until_changed());
|
||||
|
||||
resetButton->setClickedCallback([=] {
|
||||
_window->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_profile_photo_reset_sure(
|
||||
tr::now,
|
||||
lt_user,
|
||||
_user->shortName()),
|
||||
.confirmed = [=] {
|
||||
_window->session().api().peerPhoto().clearPersonal(_user);
|
||||
},
|
||||
.confirmText = tr::lng_profile_photo_reset(tr::now),
|
||||
}));
|
||||
});
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
|
||||
Ui::AddDividerText(
|
||||
inner,
|
||||
tr::lng_contact_photo_replace_info(lt_user, std::move(nameValue)));
|
||||
Ui::AddSkip(inner);
|
||||
}
|
||||
|
||||
void Controller::setupDeleteContactButton() {
|
||||
if (!_user->isContact()) {
|
||||
return;
|
||||
}
|
||||
const auto inner = _box->verticalLayout();
|
||||
const auto deleteButton = Settings::AddButtonWithIcon(
|
||||
inner,
|
||||
tr::lng_info_delete_contact(),
|
||||
st::settingsAttentionButton,
|
||||
{ nullptr });
|
||||
deleteButton->setClickedCallback([=] {
|
||||
const auto text = tr::lng_sure_delete_contact(
|
||||
tr::now,
|
||||
lt_contact,
|
||||
_user->name());
|
||||
const auto deleteSure = [=](Fn<void()> &&close) {
|
||||
close();
|
||||
_user->session().api().request(MTPcontacts_DeleteContacts(
|
||||
MTP_vector<MTPInputUser>(1, _user->inputUser)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_user->session().api().applyUpdates(result);
|
||||
_box->closeBox();
|
||||
}).send();
|
||||
};
|
||||
_window->show(Ui::MakeConfirmBox({
|
||||
.text = text,
|
||||
.confirmed = deleteSure,
|
||||
.confirmText = tr::lng_box_delete(),
|
||||
.confirmStyle = &st::attentionBoxButton,
|
||||
}));
|
||||
});
|
||||
Ui::AddSkip(inner);
|
||||
}
|
||||
|
||||
void Controller::setupSharePhoneNumber() {
|
||||
const auto settings = _user->barSettings();
|
||||
if (!settings
|
||||
@@ -279,11 +716,184 @@ void Controller::setupSharePhoneNumber() {
|
||||
|
||||
}
|
||||
|
||||
void Controller::showPhotoMenu(bool suggest) {
|
||||
_photoMenu = base::make_unique_q<Ui::PopupMenu>(
|
||||
_box,
|
||||
st::popupMenuWithIcons);
|
||||
|
||||
QObject::connect(_photoMenu.get(), &QObject::destroyed, [=] {
|
||||
finishIconAnimation(suggest);
|
||||
});
|
||||
|
||||
_photoMenu->addAction(
|
||||
tr::lng_attach_photo(tr::now),
|
||||
[=] { executeWithDelay([=] { choosePhotoFile(suggest); }, suggest); },
|
||||
&st::menuIconPhoto);
|
||||
|
||||
if (const auto data = QGuiApplication::clipboard()->mimeData()) {
|
||||
if (data->hasImage()) {
|
||||
auto callback = [=] {
|
||||
Editor::PrepareProfilePhoto(
|
||||
_box,
|
||||
&_window->window(),
|
||||
Editor::EditorData{
|
||||
.about = (suggest
|
||||
? tr::lng_profile_suggest_sure(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(_user->shortName()),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_profile_set_personal_sure(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(_user->shortName()),
|
||||
Ui::Text::WithEntities)),
|
||||
.confirm = (suggest
|
||||
? tr::lng_profile_suggest_button(tr::now)
|
||||
: tr::lng_profile_set_photo_button(tr::now)),
|
||||
.cropType = Editor::EditorData::CropType::Ellipse,
|
||||
.keepAspectRatio = true,
|
||||
},
|
||||
[=](QImage &&editedImage) {
|
||||
processChosenPhoto(std::move(editedImage), suggest);
|
||||
},
|
||||
qvariant_cast<QImage>(data->imageData()));
|
||||
};
|
||||
_photoMenu->addAction(
|
||||
tr::lng_profile_photo_from_clipboard(tr::now),
|
||||
[=] { executeWithDelay(callback, suggest); },
|
||||
&st::menuIconPhoto);
|
||||
}
|
||||
}
|
||||
|
||||
UserpicBuilder::AddEmojiBuilderAction(
|
||||
_window,
|
||||
_photoMenu.get(),
|
||||
_window->session().api().peerPhoto().emojiListValue(
|
||||
Api::PeerPhoto::EmojiListType::Profile),
|
||||
[=](UserpicBuilder::Result data) {
|
||||
processChosenPhotoWithMarkup(std::move(data), suggest);
|
||||
},
|
||||
false);
|
||||
|
||||
_photoMenu->popup(QCursor::pos());
|
||||
}
|
||||
|
||||
void Controller::choosePhotoFile(bool suggest) {
|
||||
Editor::PrepareProfilePhotoFromFile(
|
||||
_box,
|
||||
&_window->window(),
|
||||
Editor::EditorData{
|
||||
.about = (suggest
|
||||
? tr::lng_profile_suggest_sure(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(_user->shortName()),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_profile_set_personal_sure(
|
||||
tr::now,
|
||||
lt_user,
|
||||
Ui::Text::Bold(_user->shortName()),
|
||||
Ui::Text::WithEntities)),
|
||||
.confirm = (suggest
|
||||
? tr::lng_profile_suggest_button(tr::now)
|
||||
: tr::lng_profile_set_photo_button(tr::now)),
|
||||
.cropType = Editor::EditorData::CropType::Ellipse,
|
||||
.keepAspectRatio = true,
|
||||
},
|
||||
[=](QImage &&image) {
|
||||
processChosenPhoto(std::move(image), suggest);
|
||||
});
|
||||
}
|
||||
|
||||
void Controller::processChosenPhoto(QImage &&image, bool suggest) {
|
||||
Api::PeerPhoto::UserPhoto photo{
|
||||
.image = base::duplicate(image),
|
||||
};
|
||||
if (suggest && _suggestIcon && _suggestIcon->valid()) {
|
||||
_suggestIcon->animate([=] { _suggestIconWidget->update(); });
|
||||
} else if (!suggest && _cameraIcon && _cameraIcon->valid()) {
|
||||
_cameraIcon->animate([=] { _cameraIconWidget->update(); });
|
||||
}
|
||||
if (suggest) {
|
||||
_window->session().api().peerPhoto().suggest(_user, std::move(photo));
|
||||
_window->showPeerHistory(_user->id);
|
||||
} else {
|
||||
_window->session().api().peerPhoto().upload(_user, std::move(photo));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::processChosenPhotoWithMarkup(
|
||||
UserpicBuilder::Result &&data,
|
||||
bool suggest) {
|
||||
Api::PeerPhoto::UserPhoto photo{
|
||||
.image = std::move(data.image),
|
||||
.markupDocumentId = data.id,
|
||||
.markupColors = std::move(data.colors),
|
||||
};
|
||||
if (suggest && _suggestIcon && _suggestIcon->valid()) {
|
||||
_suggestIcon->animate([=] { _suggestIconWidget->update(); });
|
||||
} else if (!suggest && _cameraIcon && _cameraIcon->valid()) {
|
||||
_cameraIcon->animate([=] { _cameraIconWidget->update(); });
|
||||
}
|
||||
if (suggest) {
|
||||
_window->session().api().peerPhoto().suggest(_user, std::move(photo));
|
||||
_window->showPeerHistory(_user->id);
|
||||
} else {
|
||||
_window->session().api().peerPhoto().upload(_user, std::move(photo));
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::finishIconAnimation(bool suggest) {
|
||||
const auto icon = suggest ? _suggestIcon.get() : _cameraIcon.get();
|
||||
const auto widget = suggest ? _suggestIconWidget : _cameraIconWidget;
|
||||
if (icon && icon->valid()) {
|
||||
icon->setCustomStartFrame(icon->frameIndex());
|
||||
icon->setCustomEndFrame(-1);
|
||||
icon->animate([=] { widget->update(); });
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::executeWithDelay(
|
||||
Fn<void()> callback,
|
||||
bool suggest,
|
||||
bool startAnimation) {
|
||||
const auto icon = suggest ? _suggestIcon.get() : _cameraIcon.get();
|
||||
const auto widget = suggest ? _suggestIconWidget : _cameraIconWidget;
|
||||
|
||||
if (startAnimation && icon && icon->valid()) {
|
||||
icon->setCustomStartFrame(icon->frameIndex());
|
||||
icon->setCustomEndFrame(-1);
|
||||
icon->animate([=] { widget->update(); });
|
||||
}
|
||||
|
||||
if (icon && icon->valid() && icon->animating()) {
|
||||
base::call_delayed(50, [=] {
|
||||
executeWithDelay(callback, suggest, false);
|
||||
});
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EditContactBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<UserData*> user) {
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->lifetime().make_state<Controller>(box, window, user)->prepare();
|
||||
}
|
||||
|
||||
void EditContactNoteBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<UserData*> user) {
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->lifetime().make_state<Controller>(
|
||||
box,
|
||||
window,
|
||||
user,
|
||||
true)->prepare();
|
||||
}
|
||||
|
||||
@@ -19,3 +19,8 @@ void EditContactBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<UserData*> user);
|
||||
|
||||
void EditContactNoteBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Window::SessionController*> window,
|
||||
not_null<UserData*> user);
|
||||
|
||||
@@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/emoji_fly_animation.h"
|
||||
#include "ui/abstract_button.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_icons.h"
|
||||
@@ -77,7 +76,9 @@ DefaultIconEmoji::DefaultIconEmoji(
|
||||
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
|
||||
_icon = value;
|
||||
_image = QImage();
|
||||
repaint();
|
||||
if (repaint) {
|
||||
repaint();
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
@@ -409,9 +410,12 @@ void EditForumTopicBox(
|
||||
const auto topic = (!creating && forum->peer->forum())
|
||||
? forum->peer->forum()->topicFor(rootId)
|
||||
: nullptr;
|
||||
const auto bot = forum->peer->isBot();
|
||||
const auto created = topic && !topic->creating();
|
||||
box->setTitle(creating
|
||||
? tr::lng_forum_topic_new()
|
||||
: bot
|
||||
? tr::lng_bot_thread_edit()
|
||||
: tr::lng_forum_topic_edit());
|
||||
|
||||
box->setMaxHeight(st::editTopicMaxHeight);
|
||||
@@ -439,7 +443,9 @@ void EditForumTopicBox(
|
||||
object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::defaultInputField,
|
||||
tr::lng_forum_topic_title(),
|
||||
(bot
|
||||
? tr::lng_bot_thread_title()
|
||||
: tr::lng_forum_topic_title()),
|
||||
topic ? topic->title() : QString()),
|
||||
st::editTopicTitleMargin);
|
||||
box->setFocusCallback([=] {
|
||||
@@ -491,7 +497,9 @@ void EditForumTopicBox(
|
||||
}, title->lifetime());
|
||||
|
||||
if (!topic || !topic->isGeneral()) {
|
||||
Ui::AddDividerText(top, tr::lng_forum_choose_title_and_icon());
|
||||
Ui::AddDividerText(top, bot
|
||||
? tr::lng_bot_thread_choose_title_and_icon()
|
||||
: tr::lng_forum_choose_title_and_icon());
|
||||
|
||||
box->setScrollStyle(st::reactPanelScroll);
|
||||
|
||||
@@ -513,8 +521,7 @@ void EditForumTopicBox(
|
||||
}
|
||||
|
||||
const auto create = [=] {
|
||||
const auto channel = forum->peer->asChannel();
|
||||
if (!channel || !channel->isForum()) {
|
||||
if (!forum->peer->isForum()) {
|
||||
box->closeBox();
|
||||
return;
|
||||
} else if (title->getLastText().trimmed().isEmpty()) {
|
||||
@@ -525,7 +532,7 @@ void EditForumTopicBox(
|
||||
controller->showSection(
|
||||
std::make_shared<ChatMemento>(ChatViewId{
|
||||
.history = forum,
|
||||
.repliesRootId = channel->forum()->reserveCreatingId(
|
||||
.repliesRootId = forum->peer->forum()->reserveCreatingId(
|
||||
title->getLastText().trimmed(),
|
||||
state->defaultIcon.current().colorId,
|
||||
state->iconId.current()),
|
||||
@@ -552,13 +559,13 @@ void EditForumTopicBox(
|
||||
topic->applyIconId(state->iconId.current());
|
||||
box->closeBox();
|
||||
} else {
|
||||
using Flag = MTPchannels_EditForumTopic::Flag;
|
||||
using Flag = MTPmessages_EditForumTopic::Flag;
|
||||
const auto api = &forum->session().api();
|
||||
const auto weak = base::make_weak(box);
|
||||
state->requestId = api->request(MTPchannels_EditForumTopic(
|
||||
state->requestId = api->request(MTPmessages_EditForumTopic(
|
||||
MTP_flags(Flag::f_title
|
||||
| (topic->isGeneral() ? Flag() : Flag::f_icon_emoji_id)),
|
||||
topic->channel()->inputChannel,
|
||||
topic->peer()->input,
|
||||
MTP_int(rootId),
|
||||
MTP_string(title->getLastText().trimmed()),
|
||||
MTP_long(state->iconId.current()),
|
||||
|
||||
@@ -13,8 +13,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/peers/replace_boost_box.h"
|
||||
#include "boxes/background_box.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "boxes/transfer_gift_box.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_changes.h"
|
||||
@@ -23,13 +26,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_emoji_statuses.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_premium_limits.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_web_page.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "info/channel_statistics/boosts/info_boosts_widget.h"
|
||||
#include "info/peer_gifts/info_peer_gifts_common.h"
|
||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "iv/iv_data.h"
|
||||
@@ -42,11 +48,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/boxes/boost_box.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/controls/button_labels.h"
|
||||
#include "ui/controls/sub_tabs.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/effects/premium_graphics.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/color_contrast.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/vertical_list.h"
|
||||
@@ -54,6 +64,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/section_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_info.h" // defaultSubTabs.
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_settings.h"
|
||||
@@ -66,13 +78,16 @@ using namespace Settings;
|
||||
constexpr auto kFakeChannelId = ChannelId(0xFFFFFFF000ULL);
|
||||
constexpr auto kFakeWebPageId = WebPageId(0xFFFFFFFF00000000ULL);
|
||||
constexpr auto kSelectAnimationDuration = crl::time(150);
|
||||
constexpr auto kUnsetColorIndex = uint8(0xFF);
|
||||
|
||||
class ColorSample final : public Ui::AbstractButton {
|
||||
public:
|
||||
ColorSample(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Main::Session*> session,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
rpl::producer<uint8> colorIndex,
|
||||
rpl::producer<std::shared_ptr<Ui::ColorCollectible>> collectible,
|
||||
const QString &name);
|
||||
ColorSample(
|
||||
not_null<QWidget*> parent,
|
||||
@@ -90,6 +105,7 @@ private:
|
||||
std::shared_ptr<Ui::ChatStyle> _style;
|
||||
Ui::Text::String _name;
|
||||
uint8 _index = 0;
|
||||
std::shared_ptr<Ui::ColorCollectible> _collectible;
|
||||
Ui::Animations::Simple _selectAnimation;
|
||||
bool _selected = false;
|
||||
bool _simple = false;
|
||||
@@ -121,7 +137,8 @@ public:
|
||||
std::shared_ptr<Ui::ChatTheme> theme,
|
||||
not_null<PeerData*> peer,
|
||||
rpl::producer<uint8> colorIndexValue,
|
||||
rpl::producer<DocumentId> backgroundEmojiId);
|
||||
rpl::producer<DocumentId> backgroundEmojiId,
|
||||
rpl::producer<std::optional<Ui::ColorCollectible>> colorCollectible);
|
||||
~PreviewWrap();
|
||||
|
||||
private:
|
||||
@@ -170,16 +187,33 @@ private:
|
||||
|
||||
ColorSample::ColorSample(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Main::Session*> session,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
rpl::producer<uint8> colorIndex,
|
||||
rpl::producer<std::shared_ptr<Ui::ColorCollectible>> collectible,
|
||||
const QString &name)
|
||||
: AbstractButton(parent)
|
||||
, _style(style)
|
||||
, _name(st::semiboldTextStyle, name) {
|
||||
std::move(
|
||||
colorIndex
|
||||
) | rpl::start_with_next([=](uint8 index) {
|
||||
, _style(style) {
|
||||
rpl::combine(
|
||||
std::move(colorIndex),
|
||||
std::move(collectible)
|
||||
) | rpl::start_with_next([=](
|
||||
uint8 index,
|
||||
std::shared_ptr<Ui::ColorCollectible> collectible) {
|
||||
_index = index;
|
||||
_collectible = std::move(collectible);
|
||||
if (const auto raw = _collectible.get()) {
|
||||
_name.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
Data::SingleCustomEmoji(raw->giftEmojiId),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({
|
||||
.session = session,
|
||||
.repaint = [=] { update(); },
|
||||
}));
|
||||
} else {
|
||||
_name.setText(st::semiboldTextStyle, name);
|
||||
}
|
||||
setNaturalWidth([&] {
|
||||
if (_name.isEmpty() || _style->colorPatternIndex(_index)) {
|
||||
return st::settingsColorSampleSize;
|
||||
@@ -225,7 +259,21 @@ void ColorSample::paintEvent(QPaintEvent *e) {
|
||||
if (!_simple && !colors.outlines[1].alpha()) {
|
||||
const auto radius = height() / 2;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(colors.bg);
|
||||
if (const auto raw = _collectible.get()) {
|
||||
const auto withBg = [&](const QColor &color) {
|
||||
return Ui::CountContrast(st::windowBg->c, color);
|
||||
};
|
||||
const auto dark = (withBg({ 0, 0, 0 })
|
||||
< withBg({ 255, 255, 255 }));
|
||||
const auto name = (dark && raw->darkAccentColor.alpha() > 0)
|
||||
? raw->darkAccentColor
|
||||
: raw->accentColor;
|
||||
auto bg = name;
|
||||
bg.setAlpha(0.12 * 255);
|
||||
p.setBrush(bg);
|
||||
} else {
|
||||
p.setBrush(colors.bg);
|
||||
}
|
||||
p.drawRoundedRect(rect(), radius, radius);
|
||||
|
||||
const auto padding = st::settingsColorSamplePadding;
|
||||
@@ -291,7 +339,8 @@ PreviewWrap::PreviewWrap(
|
||||
std::shared_ptr<Ui::ChatTheme> theme,
|
||||
not_null<PeerData*> peer,
|
||||
rpl::producer<uint8> colorIndexValue,
|
||||
rpl::producer<DocumentId> backgroundEmojiId)
|
||||
rpl::producer<DocumentId> backgroundEmojiId,
|
||||
rpl::producer<std::optional<Ui::ColorCollectible>> colorCollectible)
|
||||
: RpWidget(box)
|
||||
, _box(box)
|
||||
, _peer(peer)
|
||||
@@ -357,13 +406,24 @@ PreviewWrap::PreviewWrap(
|
||||
|
||||
_fake->setName(peer->name(), QString());
|
||||
std::move(colorIndexValue) | rpl::start_with_next([=](uint8 index) {
|
||||
_fake->changeColorIndex(index);
|
||||
update();
|
||||
if (index != kUnsetColorIndex) {
|
||||
_fake->changeColorIndex(index);
|
||||
update();
|
||||
}
|
||||
}, lifetime());
|
||||
std::move(backgroundEmojiId) | rpl::start_with_next([=](DocumentId id) {
|
||||
_fake->changeBackgroundEmojiId(id);
|
||||
update();
|
||||
}, lifetime());
|
||||
std::move(colorCollectible) | rpl::start_with_next([=](
|
||||
std::optional<Ui::ColorCollectible> &&collectible) {
|
||||
if (collectible) {
|
||||
_fake->changeColorCollectible(std::move(*collectible));
|
||||
} else {
|
||||
_fake->clearColorCollectible();
|
||||
}
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
const auto session = &_history->session();
|
||||
session->data().viewRepaintRequest(
|
||||
@@ -517,6 +577,7 @@ void LevelBadge::paintEvent(QPaintEvent *e) {
|
||||
struct SetValues {
|
||||
uint8 colorIndex = 0;
|
||||
DocumentId backgroundEmojiId = 0;
|
||||
std::optional<Ui::ColorCollectible> colorCollectible;
|
||||
EmojiStatusId statusId;
|
||||
TimeId statusUntil = 0;
|
||||
bool statusChanged = false;
|
||||
@@ -527,16 +588,32 @@ void Set(
|
||||
SetValues values) {
|
||||
const auto wasIndex = peer->colorIndex();
|
||||
const auto wasEmojiId = peer->backgroundEmojiId();
|
||||
const auto &wasColorCollectible = peer->colorCollectible();
|
||||
|
||||
const auto setLocal = [=](uint8 index, DocumentId emojiId) {
|
||||
const auto setLocal = [=](
|
||||
uint8 index,
|
||||
DocumentId emojiId,
|
||||
std::optional<Ui::ColorCollectible> colorCollectible) {
|
||||
using UpdateFlag = Data::PeerUpdate::Flag;
|
||||
peer->changeColorIndex(index);
|
||||
if (index == kUnsetColorIndex) {
|
||||
peer->clearColorIndex();
|
||||
} else {
|
||||
peer->changeColorIndex(index);
|
||||
}
|
||||
if (colorCollectible) {
|
||||
peer->changeColorCollectible(*colorCollectible);
|
||||
} else {
|
||||
peer->clearColorCollectible();
|
||||
}
|
||||
peer->changeBackgroundEmojiId(emojiId);
|
||||
peer->session().changes().peerUpdated(
|
||||
peer,
|
||||
UpdateFlag::Color | UpdateFlag::BackgroundEmoji);
|
||||
};
|
||||
setLocal(values.colorIndex, values.backgroundEmojiId);
|
||||
setLocal(
|
||||
values.colorIndex,
|
||||
values.backgroundEmojiId,
|
||||
values.colorCollectible);
|
||||
|
||||
const auto done = [=] {
|
||||
show->showToast(peer->isSelf()
|
||||
@@ -546,7 +623,9 @@ void Set(
|
||||
const auto fail = [=](const MTP::Error &error) {
|
||||
const auto type = error.type();
|
||||
if (type != u"CHAT_NOT_MODIFIED"_q) {
|
||||
setLocal(wasIndex, wasEmojiId);
|
||||
setLocal(wasIndex, wasEmojiId, wasColorCollectible
|
||||
? *wasColorCollectible
|
||||
: std::optional<Ui::ColorCollectible>());
|
||||
show->showToast(type);
|
||||
}
|
||||
};
|
||||
@@ -557,10 +636,18 @@ void Set(
|
||||
};
|
||||
if (peer->isSelf()) {
|
||||
using Flag = MTPaccount_UpdateColor::Flag;
|
||||
using ColorFlag = MTPDpeerColor::Flag;
|
||||
send(MTPaccount_UpdateColor(
|
||||
MTP_flags(Flag::f_color | Flag::f_background_emoji_id),
|
||||
MTP_int(values.colorIndex),
|
||||
MTP_long(values.backgroundEmojiId)));
|
||||
MTP_flags(Flag::f_color),
|
||||
(values.colorCollectible
|
||||
? MTP_inputPeerColorCollectible(
|
||||
MTP_long(values.colorCollectible->collectibleId))
|
||||
: MTP_peerColor(
|
||||
MTP_flags(ColorFlag()
|
||||
| ColorFlag::f_color
|
||||
| ColorFlag::f_background_emoji_id),
|
||||
MTP_int(values.colorIndex),
|
||||
MTP_long(values.backgroundEmojiId)))));
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
if (peer->isBroadcast()) {
|
||||
using Flag = MTPchannels_UpdateColor::Flag;
|
||||
@@ -582,29 +669,38 @@ void Set(
|
||||
}
|
||||
}
|
||||
|
||||
bool ShowPremiumToast(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer) {
|
||||
if (!peer->isSelf() || show->session().premium()) {
|
||||
return false;
|
||||
}
|
||||
Settings::ShowPremiumPromoToast(
|
||||
show,
|
||||
tr::lng_settings_color_subscribe(
|
||||
tr::now,
|
||||
lt_link,
|
||||
Ui::Text::Link(
|
||||
Ui::Text::Bold(
|
||||
tr::lng_send_as_premium_required_link(tr::now))),
|
||||
Ui::Text::WithEntities),
|
||||
u"name_color"_q);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Apply(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
not_null<PeerData*> peer,
|
||||
SetValues values,
|
||||
Fn<void()> close,
|
||||
Fn<void()> cancel) {
|
||||
const auto session = &peer->session();
|
||||
if (peer->colorIndex() == values.colorIndex
|
||||
&& peer->backgroundEmojiId() == values.backgroundEmojiId
|
||||
&& (!peer->colorCollectible() == !values.colorCollectible)
|
||||
&& (!peer->colorCollectible()
|
||||
|| (*peer->colorCollectible() == *values.colorCollectible))
|
||||
&& !values.statusChanged) {
|
||||
close();
|
||||
} else if (peer->isSelf() && !session->premium()) {
|
||||
Settings::ShowPremiumPromoToast(
|
||||
show,
|
||||
tr::lng_settings_color_subscribe(
|
||||
tr::now,
|
||||
lt_link,
|
||||
Ui::Text::Link(
|
||||
Ui::Text::Bold(
|
||||
tr::lng_send_as_premium_required_link(tr::now))),
|
||||
Ui::Text::WithEntities),
|
||||
u"name_color"_q);
|
||||
cancel();
|
||||
} else if (peer->isSelf()) {
|
||||
Set(show, peer, values);
|
||||
close();
|
||||
@@ -658,7 +754,7 @@ public:
|
||||
not_null<Ui::GenericBox*> box,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
rpl::producer<std::vector<uint8>> indices,
|
||||
uint8 index,
|
||||
rpl::producer<uint8> index,
|
||||
Fn<void(uint8)> callback);
|
||||
|
||||
private:
|
||||
@@ -669,7 +765,7 @@ private:
|
||||
const std::shared_ptr<Ui::ChatStyle> _style;
|
||||
std::vector<std::unique_ptr<ColorSample>> _samples;
|
||||
const Fn<void(uint8)> _callback;
|
||||
uint8 _index = 0;
|
||||
rpl::variable<uint8> _index;
|
||||
|
||||
};
|
||||
|
||||
@@ -677,12 +773,12 @@ ColorSelector::ColorSelector(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
std::shared_ptr<Ui::ChatStyle> style,
|
||||
rpl::producer<std::vector<uint8>> indices,
|
||||
uint8 index,
|
||||
rpl::producer<uint8> index,
|
||||
Fn<void(uint8)> callback)
|
||||
: RpWidget(box)
|
||||
, _style(style)
|
||||
, _callback(std::move(callback))
|
||||
, _index(index) {
|
||||
, _index(std::move(index)) {
|
||||
std::move(
|
||||
indices
|
||||
) | rpl::start_with_next([=](std::vector<uint8> indices) {
|
||||
@@ -692,6 +788,7 @@ ColorSelector::ColorSelector(
|
||||
|
||||
void ColorSelector::fillFrom(std::vector<uint8> indices) {
|
||||
auto samples = std::vector<std::unique_ptr<ColorSample>>();
|
||||
const auto initial = _index.current();
|
||||
const auto add = [&](uint8 index) {
|
||||
auto i = ranges::find(_samples, index, &ColorSample::index);
|
||||
if (i != end(_samples)) {
|
||||
@@ -702,37 +799,36 @@ void ColorSelector::fillFrom(std::vector<uint8> indices) {
|
||||
this,
|
||||
_style,
|
||||
index,
|
||||
index == _index));
|
||||
index == initial));
|
||||
samples.back()->show();
|
||||
samples.back()->setClickedCallback([=] {
|
||||
if (_index != index) {
|
||||
_callback(index);
|
||||
|
||||
ranges::find(
|
||||
_samples,
|
||||
_index,
|
||||
&ColorSample::index
|
||||
)->get()->setSelected(false);
|
||||
_index = index;
|
||||
ranges::find(
|
||||
_samples,
|
||||
_index,
|
||||
&ColorSample::index
|
||||
)->get()->setSelected(true);
|
||||
}
|
||||
_callback(index);
|
||||
});
|
||||
}
|
||||
};
|
||||
for (const auto index : indices) {
|
||||
add(index);
|
||||
}
|
||||
if (!ranges::contains(indices, _index)) {
|
||||
add(_index);
|
||||
if (initial != kUnsetColorIndex && !ranges::contains(indices, initial)) {
|
||||
add(initial);
|
||||
}
|
||||
_samples = std::move(samples);
|
||||
if (width() > 0) {
|
||||
resizeToWidth(width());
|
||||
}
|
||||
|
||||
_index.value(
|
||||
) | rpl::combine_previous(
|
||||
) | rpl::start_with_next([=](uint8 was, uint8 now) {
|
||||
const auto i = ranges::find(_samples, was, &ColorSample::index);
|
||||
if (i != end(_samples)) {
|
||||
i->get()->setSelected(false);
|
||||
}
|
||||
const auto j = ranges::find(_samples, now, &ColorSample::index);
|
||||
if (j != end(_samples)) {
|
||||
j->get()->setSelected(true);
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
int ColorSelector::resizeGetHeight(int newWidth) {
|
||||
@@ -840,7 +936,7 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||
}
|
||||
auto p = QPainter(right);
|
||||
const auto height = right->height();
|
||||
if (state->emoji) {
|
||||
if (state->emoji && state->index != kUnsetColorIndex) {
|
||||
const auto colors = style->coloredValues(false, state->index);
|
||||
state->emoji->paint(p, {
|
||||
.textColor = colors.name,
|
||||
@@ -1121,6 +1217,336 @@ int ColorSelector::resizeGetHeight(int newWidth) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddColorGiftTabs(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
Fn<void(uint64 giftId)> chosen) {
|
||||
using namespace Info::PeerGifts;
|
||||
|
||||
struct State {
|
||||
rpl::variable<std::vector<Data::StarGift>> list;
|
||||
Ui::SubTabs *tabs = nullptr;
|
||||
};
|
||||
const auto state = container->lifetime().make_state<State>();
|
||||
|
||||
GiftsStars(
|
||||
session,
|
||||
session->user()
|
||||
) | rpl::start_with_next([=](const std::vector<GiftTypeStars> &list) {
|
||||
auto filtered = std::vector<Data::StarGift>();
|
||||
for (const auto &gift : list) {
|
||||
if (gift.info.peerColorAvailable && gift.resale) {
|
||||
filtered.push_back(gift.info);
|
||||
}
|
||||
}
|
||||
state->list = std::move(filtered);
|
||||
}, container->lifetime());
|
||||
|
||||
state->list.value(
|
||||
) | rpl::start_with_next([=](const std::vector<Data::StarGift> &list) {
|
||||
auto tabs = std::vector<Ui::SubTabs::Tab>();
|
||||
tabs.push_back({
|
||||
.id = u"my"_q,
|
||||
.text = tr::lng_gift_stars_tabs_my(tr::now, Ui::Text::WithEntities),
|
||||
});
|
||||
for (const auto &gift : list) {
|
||||
auto text = TextWithEntities();
|
||||
tabs.push_back({
|
||||
.id = QString::number(gift.id),
|
||||
.text = Data::SingleCustomEmoji(
|
||||
gift.document).append(' ').append(gift.resellTitle),
|
||||
});
|
||||
}
|
||||
const auto context = Core::TextContext({
|
||||
.session = session,
|
||||
});
|
||||
if (!state->tabs) {
|
||||
state->tabs = container->add(
|
||||
object_ptr<Ui::SubTabs>(
|
||||
container,
|
||||
st::defaultSubTabs,
|
||||
Ui::SubTabs::Options{
|
||||
.selected = u"my"_q,
|
||||
.centered = true,
|
||||
},
|
||||
std::move(tabs),
|
||||
context));
|
||||
|
||||
state->tabs->activated(
|
||||
) | rpl::start_with_next([=](const QString &id) {
|
||||
state->tabs->setActiveTab(id);
|
||||
chosen(id.toULongLong());
|
||||
}, state->tabs->lifetime());
|
||||
} else {
|
||||
state->tabs->setTabs(std::move(tabs), context);
|
||||
}
|
||||
container->resizeToWidth(container->width());
|
||||
}, container->lifetime());
|
||||
}
|
||||
|
||||
void AddGiftSelector(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<uint64> showingGiftIdValue,
|
||||
Fn<void(std::shared_ptr<Data::UniqueGift> selected)> chosen,
|
||||
rpl::producer<std::optional<Ui::ColorCollectible>> selected) {
|
||||
using namespace Info::PeerGifts;
|
||||
|
||||
const auto raw = container->add(
|
||||
object_ptr<Ui::VisibleRangeWidget>(container));
|
||||
|
||||
struct List {
|
||||
std::vector<GiftTypeStars> list;
|
||||
rpl::lifetime loading;
|
||||
QString offset;
|
||||
bool loaded = false;
|
||||
};
|
||||
struct State {
|
||||
std::optional<Delegate> delegate;
|
||||
rpl::variable<uint64> showingGiftId;
|
||||
base::flat_map<uint64, List> lists;
|
||||
List *current = nullptr;
|
||||
std::vector<bool> validated;
|
||||
std::vector<std::unique_ptr<GiftButton>> buttons;
|
||||
rpl::variable<Ui::VisibleRange> visibleRange;
|
||||
rpl::variable<std::optional<Ui::ColorCollectible>> selected;
|
||||
int perRow = 1;
|
||||
|
||||
Fn<void()> loadMore;
|
||||
Fn<void()> resize;
|
||||
Fn<void()> rebuild;
|
||||
};
|
||||
const auto state = raw->lifetime().make_state<State>();
|
||||
state->delegate.emplace(session, GiftButtonMode::Full);
|
||||
state->showingGiftId = std::move(showingGiftIdValue);
|
||||
state->selected = std::move(selected);
|
||||
const auto shadow = st::defaultDropdownMenu.wrap.shadow;
|
||||
const auto extend = shadow.extend;
|
||||
state->loadMore = [=] {
|
||||
const auto selfId = session->userPeerId();
|
||||
const auto shownGiftId = state->showingGiftId.current();
|
||||
if (state->current->loaded || state->current->loading) {
|
||||
return;
|
||||
} else if (shownGiftId) {
|
||||
state->current->loading = Data::ResaleGiftsSlice(
|
||||
session,
|
||||
shownGiftId,
|
||||
{},
|
||||
state->current->offset
|
||||
) | rpl::start_with_next([=](Data::ResaleGiftsDescriptor slice) {
|
||||
auto &entry = state->lists[shownGiftId];
|
||||
entry.loading.destroy();
|
||||
entry.offset = slice.offset;
|
||||
entry.loaded = entry.offset.isEmpty();
|
||||
if (state->showingGiftId.current() != shownGiftId) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &list = state->current->list;
|
||||
for (const auto &gift : slice.list) {
|
||||
if (gift.unique && gift.unique->peerColor) {
|
||||
list.push_back({
|
||||
.info = gift,
|
||||
.resale = true,
|
||||
.mine = (gift.unique->ownerId == selfId),
|
||||
});
|
||||
}
|
||||
}
|
||||
state->resize();
|
||||
});
|
||||
} else {
|
||||
state->current->loading = Data::MyUniqueGiftsSlice(
|
||||
session,
|
||||
Data::MyUniqueType::OwnedAndHosted,
|
||||
state->current->offset
|
||||
) | rpl::start_with_next([=](Data::MyGiftsDescriptor slice) {
|
||||
auto &entry = state->lists[shownGiftId];
|
||||
entry.loading.destroy();
|
||||
entry.offset = slice.offset;
|
||||
entry.loaded = entry.offset.isEmpty();
|
||||
if (state->showingGiftId.current() != shownGiftId) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &list = state->current->list;
|
||||
for (const auto &gift : slice.list) {
|
||||
if (gift.info.unique && gift.info.unique->peerColor) {
|
||||
list.push_back({ .info = gift.info });
|
||||
}
|
||||
}
|
||||
state->resize();
|
||||
});
|
||||
}
|
||||
};
|
||||
state->rebuild = [=] {
|
||||
const auto shownGiftId = state->showingGiftId.current();
|
||||
const auto width = st::boxWideWidth;
|
||||
const auto padding = st::giftBoxPadding;
|
||||
const auto available = width - padding.left() - padding.right();
|
||||
const auto range = state->visibleRange.current();
|
||||
const auto count = int(state->current->list.size());
|
||||
|
||||
auto &buttons = state->buttons;
|
||||
if (buttons.size() < count) {
|
||||
buttons.resize(count);
|
||||
}
|
||||
auto &validated = state->validated;
|
||||
validated.resize(count);
|
||||
|
||||
auto x = padding.left();
|
||||
auto y = padding.top();
|
||||
const auto single = state->delegate->buttonSize();
|
||||
const auto perRow = state->perRow;
|
||||
const auto singlew = single.width() + st::giftBoxGiftSkip.x();
|
||||
const auto singleh = single.height() + st::giftBoxGiftSkip.y();
|
||||
const auto rowFrom = std::max(range.top - y, 0) / singleh;
|
||||
const auto rowTill = (std::max(range.bottom - y + st::giftBoxGiftSkip.y(), 0) + singleh - 1)
|
||||
/ singleh;
|
||||
Assert(rowTill >= rowFrom);
|
||||
const auto first = rowFrom * perRow;
|
||||
const auto last = std::min(rowTill * perRow, count);
|
||||
const auto current = state->selected.current();
|
||||
const auto selectedId = current ? current->collectibleId : 0;
|
||||
auto checkedFrom = 0;
|
||||
auto checkedTill = int(buttons.size());
|
||||
const auto ensureButton = [&](int index) {
|
||||
auto &button = buttons[index];
|
||||
if (!button) {
|
||||
validated[index] = false;
|
||||
for (; checkedFrom != first; ++checkedFrom) {
|
||||
if (buttons[checkedFrom]) {
|
||||
button = std::move(buttons[checkedFrom]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!button) {
|
||||
for (; checkedTill != last; ) {
|
||||
--checkedTill;
|
||||
if (buttons[checkedTill]) {
|
||||
button = std::move(buttons[checkedTill]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!button) {
|
||||
const auto delegate = &*state->delegate;
|
||||
button = std::make_unique<GiftButton>(raw, delegate);
|
||||
}
|
||||
const auto raw = button.get();
|
||||
if (validated[index]) {
|
||||
return;
|
||||
}
|
||||
raw->show();
|
||||
validated[index] = true;
|
||||
const auto &gift = state->current->list[index];
|
||||
raw->setDescriptor({ gift }, shownGiftId
|
||||
? GiftButtonMode::Full
|
||||
: GiftButtonMode::Minimal);
|
||||
raw->setClickedCallback([=, unique = gift.info.unique] {
|
||||
chosen(unique);
|
||||
});
|
||||
raw->setGeometry(QRect(QPoint(x, y), single), extend);
|
||||
raw->toggleSelected(
|
||||
gift.info.unique->id == selectedId,
|
||||
GiftSelectionMode::Inset,
|
||||
anim::type::instant);
|
||||
};
|
||||
y += rowFrom * singleh;
|
||||
for (auto row = rowFrom; row != rowTill; ++row) {
|
||||
for (auto col = 0; col != perRow; ++col) {
|
||||
const auto index = row * perRow + col;
|
||||
if (index >= count) {
|
||||
break;
|
||||
}
|
||||
const auto last = !((col + 1) % perRow);
|
||||
if (last) {
|
||||
x = padding.left() + available - single.width();
|
||||
}
|
||||
ensureButton(index);
|
||||
if (last) {
|
||||
x = padding.left();
|
||||
y += singleh;
|
||||
} else {
|
||||
x += singlew;
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto till = std::min(int(buttons.size()), rowTill * perRow);
|
||||
for (auto i = count; i < till; ++i) {
|
||||
if (const auto button = buttons[i].get()) {
|
||||
button->hide();
|
||||
}
|
||||
}
|
||||
|
||||
state->selected.value(
|
||||
) | rpl::combine_previous() | rpl::start_with_next([=](
|
||||
const std::optional<Ui::ColorCollectible> &was,
|
||||
const std::optional<Ui::ColorCollectible> &now) {
|
||||
const auto wasId = was ? was->collectibleId : 0;
|
||||
const auto nowId = now ? now->collectibleId : 0;
|
||||
const auto find = [&](uint64 id) -> GiftButton* {
|
||||
if (!id) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto i = 0, count = int(state->current->list.size())
|
||||
; i != count
|
||||
; ++i) {
|
||||
const auto &gift = state->current->list[i];
|
||||
if (gift.info.unique->id == id) {
|
||||
return state->buttons[i].get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
if (const auto button = find(wasId)) {
|
||||
button->toggleSelected(false, GiftSelectionMode::Inset);
|
||||
}
|
||||
if (const auto button = find(nowId)) {
|
||||
button->toggleSelected(true, GiftSelectionMode::Inset);
|
||||
}
|
||||
}, raw->lifetime());
|
||||
|
||||
const auto page = range.bottom - range.top;
|
||||
if (page > 0 && range.bottom + page > raw->height()) {
|
||||
state->loadMore();
|
||||
}
|
||||
};
|
||||
|
||||
const auto width = st::boxWideWidth;
|
||||
const auto padding = st::giftBoxPadding;
|
||||
const auto available = width - padding.left() - padding.right();
|
||||
state->perRow = available / state->delegate->buttonSize().width();
|
||||
|
||||
state->resize = [=] {
|
||||
const auto count = int(state->current->list.size());
|
||||
state->validated.clear();
|
||||
|
||||
const auto rows = (count + state->perRow - 1) / state->perRow;
|
||||
const auto height = padding.top()
|
||||
+ (rows * state->delegate->buttonSize().height())
|
||||
+ ((rows - 1) * st::giftBoxGiftSkip.y())
|
||||
+ padding.bottom();
|
||||
raw->resize(raw->width(), height);
|
||||
|
||||
state->rebuild();
|
||||
};
|
||||
|
||||
state->showingGiftId.value(
|
||||
) | rpl::start_with_next([=](uint64 showingId) {
|
||||
state->current = &state->lists[showingId];
|
||||
state->buttons.clear();
|
||||
state->delegate.emplace(session, showingId
|
||||
? GiftButtonMode::Full
|
||||
: GiftButtonMode::Minimal);
|
||||
state->resize();
|
||||
}, raw->lifetime());
|
||||
|
||||
state->visibleRange = raw->visibleRange();
|
||||
state->visibleRange.value(
|
||||
) | rpl::start_with_next(state->rebuild, raw->lifetime());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void AddLevelBadge(
|
||||
@@ -1169,20 +1595,32 @@ void EditPeerColorBox(
|
||||
? tr::lng_settings_color_title()
|
||||
: tr::lng_edit_channel_color());
|
||||
box->setWidth(st::boxWideWidth);
|
||||
box->setStyle(st::giftBox);
|
||||
box->addTopButton(st::boxTitleClose, [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
|
||||
struct State {
|
||||
rpl::variable<uint8> index;
|
||||
rpl::variable<DocumentId> emojiId;
|
||||
rpl::variable<EmojiStatusId> statusId;
|
||||
rpl::variable<std::optional<Ui::ColorCollectible>> collectible;
|
||||
rpl::variable<uint64> showingGiftId;
|
||||
std::shared_ptr<Data::UniqueGift> buyCollectible;
|
||||
TimeId statusUntil = 0;
|
||||
bool statusChanged = false;
|
||||
bool changing = false;
|
||||
bool applying = false;
|
||||
};
|
||||
const auto state = box->lifetime().make_state<State>();
|
||||
state->index = peer->colorIndex();
|
||||
state->index = peer->colorCollectible()
|
||||
? kUnsetColorIndex
|
||||
: peer->colorIndex();
|
||||
state->emojiId = peer->backgroundEmojiId();
|
||||
state->statusId = peer->emojiStatusId();
|
||||
state->collectible = peer->colorCollectible()
|
||||
? *peer->colorCollectible()
|
||||
: std::optional<Ui::ColorCollectible>();
|
||||
if (group) {
|
||||
Settings::AddDividerTextWithLottie(box->verticalLayout(), {
|
||||
.lottie = u"palette"_q,
|
||||
@@ -1199,8 +1637,9 @@ void EditPeerColorBox(
|
||||
theme,
|
||||
peer,
|
||||
state->index.value(),
|
||||
state->emojiId.value()
|
||||
), {});
|
||||
state->emojiId.value(),
|
||||
state->collectible.value()
|
||||
), style::margins());
|
||||
|
||||
auto indices = peer->session().api().peerColors().suggestedValue();
|
||||
const auto margin = st::settingsColorRadioMargin;
|
||||
@@ -1210,8 +1649,15 @@ void EditPeerColorBox(
|
||||
box,
|
||||
style,
|
||||
std::move(indices),
|
||||
state->index.current(),
|
||||
[=](uint8 index) { state->index = index; }),
|
||||
state->index.value(),
|
||||
[=](uint8 index) {
|
||||
if (state->collectible.current()) {
|
||||
state->buyCollectible = nullptr;
|
||||
state->collectible = std::nullopt;
|
||||
state->emojiId = 0;
|
||||
}
|
||||
state->index = index;
|
||||
}),
|
||||
{ margin, skip, margin, skip });
|
||||
|
||||
Ui::AddDividerText(
|
||||
@@ -1221,10 +1667,15 @@ void EditPeerColorBox(
|
||||
: tr::lng_settings_color_about_channel()),
|
||||
st::peerAppearanceDividerTextMargin);
|
||||
|
||||
Ui::AddSkip(container, st::settingsColorSampleSkip);
|
||||
const auto iconWrap = container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
container,
|
||||
object_ptr<Ui::VerticalLayout>(container)));
|
||||
const auto iconInner = iconWrap->entity();
|
||||
|
||||
container->add(CreateEmojiIconButton(
|
||||
container,
|
||||
Ui::AddSkip(iconInner, st::settingsColorSampleSkip);
|
||||
iconInner->add(CreateEmojiIconButton(
|
||||
iconInner,
|
||||
show,
|
||||
style,
|
||||
peer,
|
||||
@@ -1232,13 +1683,19 @@ void EditPeerColorBox(
|
||||
state->emojiId.value(),
|
||||
[=](DocumentId id) { state->emojiId = id; }));
|
||||
|
||||
Ui::AddSkip(container, st::settingsColorSampleSkip);
|
||||
Ui::AddSkip(iconInner, st::settingsColorSampleSkip);
|
||||
Ui::AddDividerText(
|
||||
container,
|
||||
iconInner,
|
||||
(peer->isSelf()
|
||||
? tr::lng_settings_color_emoji_about()
|
||||
: tr::lng_settings_color_emoji_about_channel()),
|
||||
st::peerAppearanceDividerTextMargin);
|
||||
|
||||
iconWrap->toggleOn(state->collectible.value(
|
||||
) | rpl::map([](const std::optional<Ui::ColorCollectible> &value) {
|
||||
return !value.has_value();
|
||||
}));
|
||||
iconWrap->finishAnimating();
|
||||
}
|
||||
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
@@ -1324,28 +1781,108 @@ void EditPeerColorBox(
|
||||
? tr::lng_edit_channel_status_about_group()
|
||||
: tr::lng_edit_channel_status_about()),
|
||||
st::peerAppearanceDividerTextMargin);
|
||||
} else if (peer->isSelf()) {
|
||||
Ui::AddSkip(container, st::settingsColorSampleSkip);
|
||||
|
||||
const auto session = &peer->session();
|
||||
AddColorGiftTabs(container, session, [=](uint64 giftId) {
|
||||
state->showingGiftId = giftId;
|
||||
});
|
||||
|
||||
auto showingGiftId = state->showingGiftId.value();
|
||||
AddGiftSelector(container, session, std::move(showingGiftId), [=](
|
||||
std::shared_ptr<Data::UniqueGift> selected) {
|
||||
state->index = selected->peerColor ? kUnsetColorIndex : 0;
|
||||
state->emojiId = selected->peerColor
|
||||
? selected->peerColor->backgroundEmojiId
|
||||
: 0;
|
||||
state->buyCollectible = (selected->peerColor
|
||||
&& (selected->ownerId != session->userPeerId())
|
||||
&& selected->starsForResale > 0)
|
||||
? selected
|
||||
: nullptr;
|
||||
state->collectible = selected->peerColor
|
||||
? *selected->peerColor
|
||||
: std::optional<Ui::ColorCollectible>();
|
||||
}, state->collectible.value());
|
||||
}
|
||||
|
||||
box->addButton(tr::lng_settings_apply(), [=] {
|
||||
const auto button = box->addButton(tr::lng_settings_color_apply(), [=] {
|
||||
if (state->applying) {
|
||||
return;
|
||||
} else if (ShowPremiumToast(show, peer)) {
|
||||
return;
|
||||
}
|
||||
state->applying = true;
|
||||
Apply(show, peer, {
|
||||
const auto values = SetValues{
|
||||
state->index.current(),
|
||||
state->emojiId.current(),
|
||||
state->collectible.current(),
|
||||
state->statusId.current(),
|
||||
state->statusUntil,
|
||||
state->statusChanged,
|
||||
}, crl::guard(box, [=] {
|
||||
};
|
||||
if (const auto buy = state->buyCollectible) {
|
||||
const auto done = [=, weak = base::make_weak(box)](bool ok) {
|
||||
if (ok) {
|
||||
if (const auto strong = weak.get()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
Apply(show, peer, values, [] {}, [] {});
|
||||
}
|
||||
};
|
||||
const auto to = peer->session().user();
|
||||
ShowBuyResaleGiftBox(show, buy, false, to, done);
|
||||
return;
|
||||
}
|
||||
state->applying = true;
|
||||
Apply(show, peer, values, crl::guard(box, [=] {
|
||||
box->closeBox();
|
||||
}), crl::guard(box, [=] {
|
||||
state->applying = false;
|
||||
}));
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
state->collectible.value(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto buy = state->buyCollectible.get();
|
||||
while (!button->children().isEmpty()) {
|
||||
delete button->children().first();
|
||||
}
|
||||
if (!buy) {
|
||||
button->setText(rpl::combine(
|
||||
tr::lng_settings_color_apply(),
|
||||
Data::AmPremiumValue(&peer->session())
|
||||
) | rpl::map([=](const QString &text, bool premium) {
|
||||
auto result = TextWithEntities();
|
||||
if (!premium && peer->isSelf()) {
|
||||
result.append(Ui::Text::IconEmoji(&st::giftBoxLock));
|
||||
}
|
||||
result.append(text);
|
||||
return result;
|
||||
}));
|
||||
} else if (buy->onlyAcceptTon) {
|
||||
button->setText(rpl::single(QString()));
|
||||
Ui::SetButtonTwoLabels(
|
||||
button,
|
||||
tr::lng_gift_buy_resale_button(
|
||||
lt_cost,
|
||||
rpl::single(Data::FormatGiftResaleTon(*buy)),
|
||||
Ui::Text::WithEntities),
|
||||
tr::lng_gift_buy_resale_equals(
|
||||
lt_cost,
|
||||
rpl::single(Ui::Text::IconEmoji(
|
||||
&st::starIconEmojiSmall
|
||||
).append(Lang::FormatCountDecimal(buy->starsForResale))),
|
||||
Ui::Text::WithEntities),
|
||||
st::resaleButtonTitle,
|
||||
st::resaleButtonSubtitle);
|
||||
} else {
|
||||
button->setText(tr::lng_gift_buy_resale_button(
|
||||
lt_cost,
|
||||
rpl::single(Ui::Text::IconEmoji(&st::starIconEmoji).append(
|
||||
Lang::FormatCountDecimal(buy->starsForResale))),
|
||||
Ui::Text::WithEntities));
|
||||
}
|
||||
}, button->lifetime());
|
||||
}
|
||||
|
||||
void SetupPeerColorSample(
|
||||
@@ -1359,12 +1896,20 @@ void SetupPeerColorSample(
|
||||
) | rpl::map([=] {
|
||||
return peer->colorIndex();
|
||||
});
|
||||
auto colorCollectibleValue = peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::Color
|
||||
) | rpl::map([=] {
|
||||
return peer->colorCollectible();
|
||||
});
|
||||
const auto name = peer->shortName();
|
||||
|
||||
const auto sample = Ui::CreateChild<ColorSample>(
|
||||
button.get(),
|
||||
&peer->session(),
|
||||
style,
|
||||
rpl::duplicate(colorIndexValue),
|
||||
rpl::duplicate(colorCollectibleValue),
|
||||
name);
|
||||
sample->show();
|
||||
|
||||
|
||||
@@ -1135,7 +1135,7 @@ void Controller::fillForumButton() {
|
||||
_forumSavedValue = _peer->isForum();
|
||||
_forumTabsSavedValue = !_peer->isChannel()
|
||||
|| !_peer->isForum()
|
||||
|| _peer->asChannel()->useSubsectionTabs();
|
||||
|| _peer->useSubsectionTabs();
|
||||
|
||||
const auto changes = std::make_shared<rpl::event_stream<>>();
|
||||
const auto label = [=] {
|
||||
|
||||
@@ -969,9 +969,9 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
|
||||
const auto photoSize = st::boostReplaceUserpic.photoSize;
|
||||
const auto session = &row->peer()->session();
|
||||
content->add(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
Settings::SubscriptionUserpic(content, channel, photoSize)));
|
||||
content->add(
|
||||
Settings::SubscriptionUserpic(content, channel, photoSize),
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -981,7 +981,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
box,
|
||||
tr::lng_credits_box_subscription_title(),
|
||||
st::creditsBoxAboutTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
Ui::AddSkip(content);
|
||||
@@ -990,7 +989,6 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
subtitle1->setMarkedText(
|
||||
tr::lng_credits_subscription_subtitle(
|
||||
@@ -1002,11 +1000,10 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
Ui::Text::WithEntities),
|
||||
_emojiHelper.context());
|
||||
const auto subtitle2 = box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
st::creditsTopupPrice)))->entity();
|
||||
st::creditsTopupPrice),
|
||||
style::al_top);
|
||||
session->credits().rateValue(
|
||||
channel
|
||||
) | rpl::start_with_next([=, currency = u"USD"_q](float64 rate) {
|
||||
@@ -1038,19 +1035,11 @@ void Controller::rowClicked(not_null<PeerListRow*> row) {
|
||||
tr::lng_credits_box_out_about_link(tr::now)),
|
||||
Ui::Text::WithEntities),
|
||||
st::creditsBoxAboutDivider),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
|
||||
const auto button = box->addButton(tr::lng_box_ok(), [=] {
|
||||
box->addButton(tr::lng_box_ok(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
const auto buttonWidth = st::boxWidth
|
||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSlowmodeValues = 7;
|
||||
constexpr auto kSlowmodeValues = 8;
|
||||
constexpr auto kBoostsUnrestrictValues = 5;
|
||||
constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
|
||||
constexpr auto kDefaultChargeStars = 10;
|
||||
@@ -188,12 +188,13 @@ int SlowmodeDelayByIndex(int index) {
|
||||
|
||||
switch (index) {
|
||||
case 0: return 0;
|
||||
case 1: return 10;
|
||||
case 2: return 30;
|
||||
case 3: return 60;
|
||||
case 4: return 5 * 60;
|
||||
case 5: return 15 * 60;
|
||||
case 6: return 60 * 60;
|
||||
case 1: return 5;
|
||||
case 2: return 10;
|
||||
case 3: return 30;
|
||||
case 4: return 60;
|
||||
case 5: return 5 * 60;
|
||||
case 6: return 15 * 60;
|
||||
case 7: return 60 * 60;
|
||||
}
|
||||
Unexpected("Index in SlowmodeDelayByIndex.");
|
||||
}
|
||||
|
||||
@@ -7,23 +7,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/peers/edit_peer_usernames_list.h"
|
||||
|
||||
#include "api/api_filter_updates.h"
|
||||
#include "api/api_user_names.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_user.h"
|
||||
#include "info/profile/info_profile_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/layers/show.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/text/text_utilities.h" // Ui::Text::RichLangValue.
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/vertical_layout_reorder.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "styles/style_boxes.h" // contactsStatusFont.
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -208,6 +211,17 @@ UsernamesList::UsernamesList(
|
||||
}
|
||||
}
|
||||
load();
|
||||
|
||||
rpl::merge(
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::Username),
|
||||
peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::Usernames)
|
||||
) | rpl::start_with_next([=] {
|
||||
load();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void UsernamesList::load() {
|
||||
@@ -250,6 +264,8 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
username.username);
|
||||
const auto status = (username.editable && _focusCallback)
|
||||
? tr::lng_usernames_edit(tr::now)
|
||||
: (username.editable && !username.active)
|
||||
? tr::lng_usernames_non_active(tr::now)
|
||||
: username.active
|
||||
? tr::lng_usernames_active(tr::now)
|
||||
: tr::lng_usernames_non_active(tr::now);
|
||||
@@ -265,8 +281,20 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
if (username.editable) {
|
||||
if (_focusCallback) {
|
||||
_focusCallback();
|
||||
return;
|
||||
}
|
||||
if (_isBot) {
|
||||
const auto hasActiveAuction = ranges::any_of(
|
||||
usernames,
|
||||
[](const Data::Username &u) {
|
||||
return !u.editable && u.active;
|
||||
});
|
||||
if (!hasActiveAuction && username.active) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto text = _peer->isSelf()
|
||||
@@ -309,10 +337,13 @@ void UsernamesList::rebuild(const Data::Usernames &usernames) {
|
||||
rpl::single(kMaxUsernames),
|
||||
Ui::Text::RichLangValue)));
|
||||
}
|
||||
if (error == Api::Usernames::Error::Flood) {
|
||||
_show->showToast(
|
||||
tr::lng_flood_error(tr::now));
|
||||
}
|
||||
load();
|
||||
_toggleLifetime.destroy();
|
||||
}, [=] {
|
||||
load();
|
||||
_toggleLifetime.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -230,9 +230,13 @@ void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
|
||||
if (fill > 0) {
|
||||
const auto t = roundedHeight + _scrollTop;
|
||||
p.drawImage(
|
||||
QRect(0, t, roundedWidth * factor, (roundedWidth - t) * factor),
|
||||
QRect(0, t, roundedWidth, roundedWidth - t),
|
||||
image,
|
||||
QRect(0, t, roundedWidth * factor, (roundedWidth - t) * factor));
|
||||
QRect(
|
||||
0,
|
||||
t * factor,
|
||||
roundedWidth * factor,
|
||||
(roundedWidth - t) * factor));
|
||||
}
|
||||
if (covered <= 0) {
|
||||
return;
|
||||
@@ -241,9 +245,9 @@ void PeerShortInfoCover::paintCoverImage(QPainter &p, const QImage &image) {
|
||||
const auto from = top - rounded;
|
||||
auto q = QPainter(&_roundedTopImage);
|
||||
q.drawImage(
|
||||
QRect(0, 0, roundedWidth * factor, rounded * factor),
|
||||
QRect(0, 0, roundedWidth, rounded),
|
||||
image,
|
||||
QRect(0, _scrollTop, roundedWidth * factor, rounded * factor));
|
||||
QRect(0, _scrollTop * factor, roundedWidth * factor, rounded * factor));
|
||||
q.end();
|
||||
_roundedTopImage = Images::Round(
|
||||
std::move(_roundedTopImage),
|
||||
@@ -811,6 +815,10 @@ void PeerShortInfoBox::prepareRows() {
|
||||
birthdayLabel(),
|
||||
birthdayValue() | Ui::Text::ToWithEntities(),
|
||||
tr::lng_mediaview_copy(tr::now));
|
||||
addInfoLine(
|
||||
tr::lng_info_notes_label(),
|
||||
noteValue(),
|
||||
_st.labeled);
|
||||
}
|
||||
|
||||
void PeerShortInfoBox::resizeEvent(QResizeEvent *e) {
|
||||
@@ -917,3 +925,9 @@ rpl::producer<TextWithEntities> PeerShortInfoBox::aboutValue() const {
|
||||
return fields.about;
|
||||
}) | rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
rpl::producer<TextWithEntities> PeerShortInfoBox::noteValue() const {
|
||||
return _fields.value() | rpl::map([](const PeerShortInfoFields &fields) {
|
||||
return fields.note;
|
||||
}) | rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ struct PeerShortInfoFields {
|
||||
TextWithEntities about;
|
||||
QString username;
|
||||
Data::Birthday birthday;
|
||||
TextWithEntities note;
|
||||
bool isBio = false;
|
||||
};
|
||||
|
||||
@@ -188,6 +189,7 @@ private:
|
||||
[[nodiscard]] rpl::producer<QString> birthdayLabel() const;
|
||||
[[nodiscard]] rpl::producer<QString> birthdayValue() const;
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> aboutValue() const;
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> noteValue() const;
|
||||
|
||||
const style::ShortInfoBox &_st;
|
||||
const PeerShortInfoType _type = PeerShortInfoType::User;
|
||||
|
||||
@@ -210,7 +210,8 @@ void ProcessFullPhoto(
|
||||
| UpdateFlag::PhoneNumber
|
||||
| UpdateFlag::Username
|
||||
| UpdateFlag::About
|
||||
| UpdateFlag::Birthday)
|
||||
| UpdateFlag::Birthday
|
||||
| UpdateFlag::ContactNote)
|
||||
) | rpl::map([=] {
|
||||
const auto user = peer->asUser();
|
||||
const auto username = peer->username();
|
||||
@@ -237,6 +238,7 @@ void ProcessFullPhoto(
|
||||
? ('@' + username)
|
||||
: QString()),
|
||||
.birthday = user ? user->birthday() : Data::Birthday(),
|
||||
.note = user ? user->note() : TextWithEntities(),
|
||||
.isBio = (user && !user->isBot()),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -11,8 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_premium_limits.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_premium_limits.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -32,8 +34,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/padding_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/empty_userpic.h"
|
||||
#include "ui/dynamic_image.h"
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_premium.h"
|
||||
|
||||
namespace {
|
||||
@@ -833,3 +837,174 @@ object_ptr<Ui::RpWidget> CreateUserpicsWithMoreBadge(
|
||||
}, overlay->lifetime());
|
||||
return result;
|
||||
}
|
||||
|
||||
class UniqueGiftBackground final : public Ui::DynamicImage {
|
||||
public:
|
||||
UniqueGiftBackground(
|
||||
not_null<Main::Session*> session,
|
||||
std::shared_ptr<Data::UniqueGift> unique)
|
||||
: _session(session)
|
||||
, _unique(std::move(unique)) {
|
||||
}
|
||||
|
||||
std::shared_ptr<Ui::DynamicImage> clone() override {
|
||||
return std::make_shared<UniqueGiftBackground>(_session, _unique);
|
||||
}
|
||||
|
||||
void subscribeToUpdates(Fn<void()> callback) override {
|
||||
_repaint = std::move(callback);
|
||||
if (!_repaint) {
|
||||
_patternEmoji = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QImage image(int size) override {
|
||||
if (!_patternEmoji) {
|
||||
_patternEmoji = _session->data().customEmojiManager().create(
|
||||
_unique->pattern.document,
|
||||
[=] { ready(); },
|
||||
Data::CustomEmojiSizeTag::Large);
|
||||
[[maybe_unused]] const auto preload = _patternEmoji->ready();
|
||||
}
|
||||
const auto inner = QRect(0, 0, size, size);
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
if (_backgroundCache.size() != inner.size() * ratio) {
|
||||
_backgroundCache = QImage(
|
||||
inner.size() * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
_backgroundCache.fill(Qt::transparent);
|
||||
_backgroundCache.setDevicePixelRatio(ratio);
|
||||
|
||||
const auto radius = st::giftBoxGiftRadius;
|
||||
auto p = QPainter(&_backgroundCache);
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
auto gradient = QRadialGradient(
|
||||
inner.center(),
|
||||
inner.width() / 2);
|
||||
gradient.setStops({
|
||||
{ 0., _unique->backdrop.centerColor },
|
||||
{ 1., _unique->backdrop.edgeColor },
|
||||
});
|
||||
p.setBrush(gradient);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.drawRoundedRect(inner, radius, radius);
|
||||
_backroundPatterned = false;
|
||||
}
|
||||
if (!_backroundPatterned && _patternEmoji->ready()) {
|
||||
_backroundPatterned = true;
|
||||
auto p = QPainter(&_backgroundCache);
|
||||
p.setClipRect(inner);
|
||||
const auto skip = inner.width() / 3;
|
||||
Ui::PaintPoints(
|
||||
p,
|
||||
Ui::PatternPointsSmall(),
|
||||
_patternCache,
|
||||
_patternEmoji.get(),
|
||||
*_unique,
|
||||
QRect(-skip, 0, inner.width() + 2 * skip, inner.height()));
|
||||
}
|
||||
return _backgroundCache;
|
||||
}
|
||||
|
||||
private:
|
||||
void ready() {
|
||||
if (!_backroundPatterned && _repaint) {
|
||||
_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const std::shared_ptr<Data::UniqueGift> _unique;
|
||||
Fn<void()> _repaint;
|
||||
std::unique_ptr<Ui::Text::CustomEmoji> _patternEmoji;
|
||||
QImage _backgroundCache;
|
||||
base::flat_map<float64, QImage> _patternCache;
|
||||
bool _backroundPatterned = false;
|
||||
|
||||
};
|
||||
|
||||
object_ptr<Ui::RpWidget> CreateGiftTransfer(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
std::shared_ptr<Data::UniqueGift> unique,
|
||||
not_null<PeerData*> to) {
|
||||
struct State {
|
||||
QImage layer;
|
||||
QPoint giftPosition;
|
||||
std::shared_ptr<UniqueGiftBackground> bg;
|
||||
std::shared_ptr<Ui::Text::CustomEmoji> sticker;
|
||||
};
|
||||
const auto st = &st::boostReplaceUserpicsRow;
|
||||
const auto full = st->button.size.height()
|
||||
+ st::boostReplaceIconAdd.y()
|
||||
+ st::lineWidth;
|
||||
auto result = object_ptr<Ui::FixedHeightWidget>(parent, full);
|
||||
const auto raw = result.data();
|
||||
const auto right = CreateChild<Ui::UserpicButton>(raw, to, st->button);
|
||||
const auto overlay = CreateChild<Ui::RpWidget>(raw);
|
||||
|
||||
const auto state = raw->lifetime().make_state<State>();
|
||||
state->bg = std::make_shared<UniqueGiftBackground>(
|
||||
&to->session(),
|
||||
unique);
|
||||
state->bg->subscribeToUpdates([=] {
|
||||
overlay->update();
|
||||
});
|
||||
const auto tag = Data::CustomEmojiSizeTag::Isolated;
|
||||
state->sticker = to->owner().customEmojiManager().create(
|
||||
unique->model.document,
|
||||
[=] { overlay->update(); },
|
||||
tag);
|
||||
overlay->update();
|
||||
|
||||
raw->widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
const auto skip = st::boostReplaceUserpicsSkip;
|
||||
const auto total = right->width() + skip + right->width();
|
||||
auto x = (width - total) / 2;
|
||||
state->giftPosition = QPoint(x, 0);
|
||||
x += right->width() + skip;
|
||||
right->moveToLeft(x, 0);
|
||||
overlay->setGeometry(QRect(0, 0, width, raw->height()));
|
||||
}, raw->lifetime());
|
||||
|
||||
overlay->paintRequest(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto outerw = overlay->width();
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
if (state->layer.size() != QSize(outerw, full) * ratio) {
|
||||
state->layer = QImage(
|
||||
QSize(outerw, full) * ratio,
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
state->layer.setDevicePixelRatio(ratio);
|
||||
}
|
||||
state->layer.fill(Qt::transparent);
|
||||
|
||||
auto q = QPainter(&state->layer);
|
||||
auto hq = PainterHighQualityEnabler(q);
|
||||
const auto from = QRect(state->giftPosition, right->size());
|
||||
const auto esize = Data::FrameSizeFromTag(tag) / ratio;
|
||||
q.drawImage(from, state->bg->image(from.width()));
|
||||
state->sticker->paint(q, {
|
||||
.textColor = st::windowFg->c,
|
||||
.now = crl::now(),
|
||||
.position = from.topLeft() + QPoint(
|
||||
(from.width() - esize) / 2,
|
||||
(from.height() - esize) / 2),
|
||||
});
|
||||
|
||||
const auto size = st::boostReplaceArrow.size();
|
||||
st::boostReplaceArrow.paint(
|
||||
q,
|
||||
(state->giftPosition.x()
|
||||
+ right->width()
|
||||
+ (st::boostReplaceUserpicsSkip - size.width()) / 2),
|
||||
(right->height() - size.height()) / 2,
|
||||
outerw);
|
||||
|
||||
q.end();
|
||||
|
||||
auto p = QPainter(overlay);
|
||||
p.drawImage(0, 0, state->layer);
|
||||
}, overlay->lifetime());
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ struct UserpicsRow;
|
||||
|
||||
class ChannelData;
|
||||
|
||||
namespace Data {
|
||||
struct UniqueGift;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@@ -72,3 +76,8 @@ enum class UserpicsTransferType {
|
||||
rpl::producer<std::vector<not_null<PeerData*>>> peers,
|
||||
const style::UserpicsRow &st,
|
||||
int limit);
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> CreateGiftTransfer(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
std::shared_ptr<Data::UniqueGift> unique,
|
||||
not_null<PeerData*> to);
|
||||
|
||||
@@ -188,7 +188,7 @@ void Controller::confirmAdd(not_null<PeerData*> peer) {
|
||||
}, field->lifetime());
|
||||
|
||||
field->setMaxLength(limit * 2);
|
||||
Ui::AddLengthLimitLabel(field, limit, std::nullopt);
|
||||
Ui::AddLengthLimitLabel(field, limit);
|
||||
|
||||
Ui::AddDividerText(box->verticalLayout(), phrases.about());
|
||||
}));
|
||||
|
||||
@@ -585,14 +585,14 @@ void ChannelsLimitBox(
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, controller),
|
||||
{});
|
||||
style::margins());
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
const auto count = 100;
|
||||
const auto placeholder = box->addRow(
|
||||
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
controller->countValue(
|
||||
@@ -676,14 +676,14 @@ void PublicLinksLimitBox(
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, controller),
|
||||
{});
|
||||
style::margins());
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
const auto count = defaultLimit;
|
||||
const auto placeholder = box->addRow(
|
||||
object_ptr<PeerListDummy>(box, count, st::defaultPeerList),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
using namespace rpl::mappers;
|
||||
controller->countValue(
|
||||
|
||||
@@ -902,7 +902,7 @@ void PreviewBox(
|
||||
|
||||
const auto outer = box->addRow(
|
||||
ChatBackPreview(box, size.height(), back),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
struct Hiding {
|
||||
not_null<Ui::RpWidget*> widget;
|
||||
|
||||
@@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/ringtones_box.h"
|
||||
|
||||
#include "data/notify/data_peer_notify_volume.h"
|
||||
#include "data/notify/data_peer_notify_settings.h"
|
||||
#include "api/api_ringtones.h"
|
||||
#include "apiwrap.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/event_filter.h"
|
||||
#include "base/timer_rpl.h"
|
||||
@@ -21,11 +24,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_document_media.h"
|
||||
#include "data/data_document_resolver.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_session_settings.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
@@ -111,7 +117,8 @@ void RingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
Data::NotifySound selected,
|
||||
Fn<void(Data::NotifySound)> save) {
|
||||
Fn<void(Data::NotifySound)> save,
|
||||
Data::VolumeController volumeController) {
|
||||
box->setTitle(tr::lng_ringtones_box_title());
|
||||
|
||||
const auto container = box->verticalLayout();
|
||||
@@ -128,12 +135,17 @@ void RingtonesBox(
|
||||
QPointer<Ui::Radiobutton> defaultButton;
|
||||
QPointer<Ui::Radiobutton> chosenButton;
|
||||
std::vector<QPointer<Ui::Radiobutton>> buttons;
|
||||
ushort presavedVolume = 0;
|
||||
};
|
||||
const auto state = container->lifetime().make_state<State>(State{
|
||||
.group = std::make_shared<Ui::RadiobuttonGroup>(),
|
||||
.chosen = selected,
|
||||
});
|
||||
|
||||
const auto volumeOverride = [volume = volumeController.volume] {
|
||||
return volume ? (0.01 * volume()) : -1;
|
||||
};
|
||||
|
||||
const auto addToGroup = [=](
|
||||
not_null<Ui::VerticalLayout*> verticalLayout,
|
||||
int value,
|
||||
@@ -156,7 +168,10 @@ void RingtonesBox(
|
||||
if (value == kDefaultValue) {
|
||||
state->defaultButton = button;
|
||||
button->setClickedCallback([=] {
|
||||
Core::App().notifications().playSound(session, 0);
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
0,
|
||||
volumeOverride());
|
||||
});
|
||||
}
|
||||
if (value < 0) {
|
||||
@@ -170,7 +185,8 @@ void RingtonesBox(
|
||||
if (media->loaded()) {
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
media->owner()->id);
|
||||
media->owner()->id,
|
||||
volumeOverride());
|
||||
}
|
||||
});
|
||||
base::install_event_filter(button, [=](not_null<QEvent*> e) {
|
||||
@@ -320,6 +336,31 @@ void RingtonesBox(
|
||||
}));
|
||||
});
|
||||
|
||||
if (volumeController.volume && volumeController.saveVolume) {
|
||||
auto saveAndTestVolume = [=](ushort currentVolume) {
|
||||
state->presavedVolume = currentVolume;
|
||||
const auto value = state->group->current();
|
||||
if (value != kNoSoundValue) {
|
||||
Core::App().notifications().playSound(
|
||||
session,
|
||||
(value == kDefaultValue)
|
||||
? 0
|
||||
: state->medias[value]->owner()->id,
|
||||
0.01 * currentVolume);
|
||||
}
|
||||
};
|
||||
Ui::AddRingtonesVolumeSlider(
|
||||
container,
|
||||
state->group->value() | rpl::map([=](int value) {
|
||||
return value != kNoSoundValue;
|
||||
}),
|
||||
tr::lng_ringtones_box_volume(),
|
||||
Data::VolumeController{
|
||||
base::take(volumeController.volume),
|
||||
std::move(saveAndTestVolume),
|
||||
});
|
||||
}
|
||||
|
||||
box->addSkip(st::ringtonesBoxSkip);
|
||||
Ui::AddDividerText(container, tr::lng_ringtones_box_about());
|
||||
|
||||
@@ -333,6 +374,9 @@ void RingtonesBox(
|
||||
: (value == kNoSoundValue)
|
||||
? Data::NotifySound{ .none = true }
|
||||
: Data::NotifySound{ .id = state->medias[value]->owner()->id };
|
||||
if (state->presavedVolume) {
|
||||
volumeController.saveVolume(state->presavedVolume);
|
||||
}
|
||||
save(sound);
|
||||
box->closeBox();
|
||||
});
|
||||
@@ -345,5 +389,5 @@ void ThreadRingtonesBox(
|
||||
const auto now = thread->owner().notifySettings().sound(thread);
|
||||
RingtonesBox(box, &thread->session(), now, [=](Data::NotifySound sound) {
|
||||
thread->owner().notifySettings().update(thread, {}, {}, sound);
|
||||
});
|
||||
}, Data::ThreadRingtonesVolumeController(thread));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Data {
|
||||
struct NotifySound;
|
||||
class Thread;
|
||||
enum class DefaultNotify : uint8_t;
|
||||
struct VolumeController;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
@@ -28,7 +30,8 @@ void RingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
not_null<Main::Session*> session,
|
||||
Data::NotifySound selected,
|
||||
Fn<void(Data::NotifySound)> save);
|
||||
Fn<void(Data::NotifySound)> save,
|
||||
Data::VolumeController volumeController);
|
||||
|
||||
void ThreadRingtonesBox(
|
||||
not_null<Ui::GenericBox*> box,
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
@@ -148,6 +149,8 @@ void SelfDestructionBox::showContent() {
|
||||
: tr::lng_self_destruct_sessions_description(tr::now)),
|
||||
st::boxLabel);
|
||||
_description->moveToLeft(st::boxPadding.left(), y);
|
||||
_description->resizeToWidth(st::boxWidth
|
||||
- rect::m::sum::h(st::boxPadding));
|
||||
y += _description->height() + st::boxMediumSkip;
|
||||
|
||||
for (const auto value : _ttlValues) {
|
||||
@@ -200,6 +203,8 @@ void SelfDestructionBox::prepare() {
|
||||
? tr::lng_self_destruct_description(tr::now)
|
||||
: tr::lng_self_destruct_sessions_description(tr::now)),
|
||||
st::boxLabel);
|
||||
fake->resizeToWidth(st::boxWidth
|
||||
- rect::m::sum::h(st::boxPadding));
|
||||
const auto boxHeight = st::boxOptionListPadding.top()
|
||||
+ fake->height() + st::boxMediumSkip
|
||||
+ (_ttlValues.size()
|
||||
|
||||
@@ -348,9 +348,9 @@ void SendCreditsBox(
|
||||
}, ministarsContainer->lifetime());
|
||||
}
|
||||
|
||||
const auto thumb = box->addRow(object_ptr<Ui::CenterWrap<>>(
|
||||
content,
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize)));
|
||||
const auto thumb = box->addRow(
|
||||
SendCreditsThumbnail(content, session, form.get(), photoSize),
|
||||
style::al_top);
|
||||
thumb->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
if (form->invoice.subscriptionPeriod) {
|
||||
const auto badge = SendCreditsBadge(content, form->invoice.amount);
|
||||
@@ -371,16 +371,14 @@ void SendCreditsBox(
|
||||
? rpl::single(form->title)
|
||||
: tr::lng_credits_box_out_title(),
|
||||
st::settingsPremiumUserTitle),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
if (form->invoice.subscriptionPeriod && form->botId && form->photo) {
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
const auto bot = session->data().user(form->botId);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<>>(
|
||||
box,
|
||||
Ui::CreatePeerBubble(box, bot)));
|
||||
Ui::CreatePeerBubble(box, bot),
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
}
|
||||
Ui::AddSkip(content);
|
||||
@@ -389,7 +387,6 @@ void SendCreditsBox(
|
||||
box,
|
||||
SendCreditsConfirmText(session, form.get()),
|
||||
st::creditsBoxAbout),
|
||||
st::boxRowPadding,
|
||||
style::al_top);
|
||||
Ui::AddSkip(content);
|
||||
Ui::AddSkip(content);
|
||||
@@ -467,14 +464,6 @@ void SendCreditsBox(
|
||||
st::creditsBoxButtonLabel,
|
||||
&box->getDelegate()->style().button.textFg);
|
||||
|
||||
const auto buttonWidth = st::boxWidth
|
||||
- rect::m::sum::h(stBox.buttonPadding);
|
||||
button->widthValue() | rpl::filter([=] {
|
||||
return (button->widthNoMargins() != buttonWidth);
|
||||
}) | rpl::start_with_next([=] {
|
||||
button->resizeToWidth(buttonWidth);
|
||||
}, button->lifetime());
|
||||
|
||||
{
|
||||
const auto close = Ui::CreateChild<Ui::IconButton>(
|
||||
content,
|
||||
|
||||
@@ -311,8 +311,17 @@ void CaptionBox(
|
||||
}
|
||||
}
|
||||
|
||||
const auto send = [=](Api::SendOptions options) {
|
||||
done(std::move(options), input->getTextWithTags());
|
||||
const auto send = [=, show = controller->uiShow()](
|
||||
Api::SendOptions options) {
|
||||
const auto textWithTags = input->getTextWithTags();
|
||||
const auto remove = Ui::ComputeFieldCharacterCount(input)
|
||||
- Data::PremiumLimits(&show->session()).captionLengthCurrent();
|
||||
if (remove > 0) {
|
||||
show->showToast(
|
||||
tr::lng_edit_limit_reached(tr::now, lt_count, remove));
|
||||
return;
|
||||
}
|
||||
done(std::move(options), textWithTags);
|
||||
};
|
||||
const auto confirm = box->addButton(
|
||||
std::move(confirmText),
|
||||
@@ -387,6 +396,16 @@ void EditCaptionBox(
|
||||
show->showToast(tr::lng_edit_error(tr::now));
|
||||
return;
|
||||
}
|
||||
const auto textLength = textWithTags.text.size();
|
||||
const auto limit = Data::PremiumLimits(
|
||||
&item->history()->session()).captionLengthCurrent();
|
||||
if (textLength > limit) {
|
||||
show->showToast(tr::lng_edit_limit_reached(
|
||||
tr::now,
|
||||
lt_count,
|
||||
textLength - limit));
|
||||
return;
|
||||
}
|
||||
auto text = TextWithEntities{
|
||||
std::move(textWithTags.text),
|
||||
ConvertTextTagsToEntities(std::move(textWithTags.tags)),
|
||||
|
||||
@@ -1733,6 +1733,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
const auto donePhraseArgs = CreateForwardedMessagePhraseArgs(
|
||||
result,
|
||||
msgIds);
|
||||
const auto showRecentForwardsToSelf = result.size() == 1
|
||||
&& result.front()->peer()->isSelf();
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
for (const auto thread : result) {
|
||||
if (!comment.text.isEmpty()) {
|
||||
@@ -1790,13 +1792,22 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
||||
Api::SuggestToMTP(options.suggest)
|
||||
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
||||
threadHistory->session().api().applyUpdates(updates);
|
||||
if (showRecentForwardsToSelf) {
|
||||
ApiWrap::ProcessRecentSelfForwards(
|
||||
&threadHistory->session(),
|
||||
updates,
|
||||
peer->id,
|
||||
history->peer->id);
|
||||
}
|
||||
state->requests.remove(reqId);
|
||||
if (state->requests.empty()) {
|
||||
if (show->valid()) {
|
||||
auto phrase = rpl::variable<TextWithEntities>(
|
||||
ChatHelpers::ForwardedMessagePhrase(
|
||||
donePhraseArgs)).current();
|
||||
show->showToast(std::move(phrase));
|
||||
if (!phrase.empty()) {
|
||||
show->showToast(std::move(phrase));
|
||||
}
|
||||
show->hideLayer();
|
||||
}
|
||||
}
|
||||
@@ -1935,6 +1946,31 @@ void FastShareMessage(
|
||||
}), Ui::LayerOption::CloseOther);
|
||||
}
|
||||
|
||||
void FastShareMessageToSelf(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto self = show->session().user();
|
||||
const auto donePhraseArgs = ChatHelpers::ForwardedMessagePhraseArgs{
|
||||
.toCount = 1,
|
||||
.singleMessage = true,
|
||||
.to1 = self,
|
||||
.to2 = nullptr,
|
||||
};
|
||||
auto sendAction = Api::SendAction(self->owner().history(self));
|
||||
sendAction.clearDraft = false;
|
||||
show->session().api().forwardMessages(
|
||||
Data::ResolvedForwardDraft{ .items = {item} },
|
||||
std::move(sendAction),
|
||||
[=] {
|
||||
auto phrase = rpl::variable<TextWithEntities>(
|
||||
ChatHelpers::ForwardedMessagePhrase(
|
||||
donePhraseArgs)).current();
|
||||
if (!phrase.empty()) {
|
||||
show->showToast(std::move(phrase));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FastShareMessage(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -71,6 +71,9 @@ struct ShareBoxStyleOverrides {
|
||||
};
|
||||
[[nodiscard]] ShareBoxStyleOverrides DarkShareBoxStyle();
|
||||
|
||||
void FastShareMessageToSelf(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item);
|
||||
void FastShareMessage(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -110,6 +110,7 @@ struct StarGiftUpgradeArgs {
|
||||
Fn<void(bool)> ready;
|
||||
not_null<PeerData*> peer;
|
||||
Data::SavedStarGiftId savedId;
|
||||
QString giftPrepayUpgradeHash;
|
||||
int cost = 0;
|
||||
bool canAddSender = false;
|
||||
bool canAddComment = false;
|
||||
@@ -157,4 +158,18 @@ void ShowResaleGiftBoughtToast(
|
||||
not_null<PeerData*> to,
|
||||
const Data::UniqueGift &gift);
|
||||
|
||||
[[nodiscard]] rpl::lifetime ShowStarGiftResale(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 giftId,
|
||||
QString title,
|
||||
Fn<void()> finishRequesting);
|
||||
|
||||
[[nodiscard]] CreditsAmount StarsFromTon(
|
||||
not_null<Main::Session*> session,
|
||||
CreditsAmount ton);
|
||||
[[nodiscard]] CreditsAmount TonFromStars(
|
||||
not_null<Main::Session*> session,
|
||||
CreditsAmount stars);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -641,24 +641,17 @@ void ChangeSetNameBox(
|
||||
const auto it = sets.find(input.id);
|
||||
return (it == sets.end()) ? QString() : it->second->title;
|
||||
}();
|
||||
const auto wrap = box->addRow(object_ptr<Ui::FixedHeightWidget>(
|
||||
const auto field = box->addRow(object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::editStickerSetNameField.heightMin));
|
||||
auto owned = object_ptr<Ui::InputField>(
|
||||
wrap,
|
||||
st::editStickerSetNameField,
|
||||
tr::lng_stickers_context_edit_name(),
|
||||
wasName);
|
||||
const auto field = owned.data();
|
||||
wrap->widthValue() | rpl::start_with_next([=](int width) {
|
||||
field->move(0, 0);
|
||||
field->resize(width, field->height());
|
||||
wrap->resize(width, field->height());
|
||||
}, wrap->lifetime());
|
||||
wasName));
|
||||
field->selectAll();
|
||||
constexpr auto kMaxSetNameLength = 50;
|
||||
field->setMaxLength(kMaxSetNameLength);
|
||||
Ui::AddLengthLimitLabel(field, kMaxSetNameLength, kMaxSetNameLength + 1);
|
||||
Ui::AddLengthLimitLabel(field, kMaxSetNameLength, {
|
||||
.customThreshold = kMaxSetNameLength + 1,
|
||||
});
|
||||
box->setFocusCallback([=] { field->setFocusFast(); });
|
||||
const auto close = crl::guard(box, [=] { box->closeBox(); });
|
||||
const auto save = [=, show = box->uiShow()] {
|
||||
@@ -757,7 +750,7 @@ void StickerSetBox::updateButtons() {
|
||||
_inner->setReorderState(true);
|
||||
updateButtons();
|
||||
},
|
||||
&st::menuIconManage);
|
||||
&st::menuIconReorder);
|
||||
});
|
||||
}();
|
||||
if (_inner->notInstalled()) {
|
||||
|
||||
@@ -11,14 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "api/api_credits.h"
|
||||
#include "api/api_cloud_password.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_star_gift.h"
|
||||
#include "data/data_user.h"
|
||||
#include "boxes/filters/edit_filter_chats_list.h" // CreatePe...tionSubtitle.
|
||||
#include "boxes/peers/replace_boost_box.h"
|
||||
#include "boxes/gift_premium_box.h"
|
||||
#include "boxes/passcode_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_star_gift.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_user.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "payments/payments_checkout_process.h"
|
||||
@@ -36,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h" // peerListSingleRow.
|
||||
#include "styles/style_credits.h" // starIconEmoji.
|
||||
#include "styles/style_dialogs.h" // recentPeersSpecialName.
|
||||
#include "styles/style_info.h" // defaultSubTabs.
|
||||
#include "styles/style_layers.h" // boxLabel.
|
||||
|
||||
namespace {
|
||||
@@ -138,6 +143,8 @@ void ExportOnBlockchain(
|
||||
) | rpl::take(
|
||||
1
|
||||
) | rpl::start_with_next([=](const Core::CloudPasswordState &pass) {
|
||||
state->lifetime.destroy();
|
||||
|
||||
auto fields = PasscodeBox::CloudFields::From(pass);
|
||||
fields.customTitle = tr::lng_gift_transfer_password_title();
|
||||
fields.customDescription
|
||||
@@ -576,17 +583,16 @@ void ShowTransferToBox(
|
||||
Fn<void()> closeParentBox) {
|
||||
const auto stars = gift->starsForTransfer;
|
||||
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_gift_transfer_title(
|
||||
lt_name,
|
||||
rpl::single(UniqueGiftName(*gift))));
|
||||
|
||||
auto transfer = (stars > 0)
|
||||
? tr::lng_gift_transfer_button_for(
|
||||
lt_price,
|
||||
tr::lng_action_gift_for_stars(
|
||||
lt_count,
|
||||
rpl::single(stars * 1.)))
|
||||
: tr::lng_gift_transfer_button();
|
||||
rpl::single(Ui::Text::IconEmoji(
|
||||
&st::starIconEmoji
|
||||
).append(Lang::FormatCreditsAmountDecimal(
|
||||
CreditsAmount(stars)
|
||||
))),
|
||||
Ui::Text::WithEntities)
|
||||
: tr::lng_gift_transfer_button(Ui::Text::WithEntities);
|
||||
|
||||
struct State {
|
||||
bool sent = false;
|
||||
@@ -619,6 +625,10 @@ void ShowTransferToBox(
|
||||
TransferGift(controller, peer, gift, savedId, done);
|
||||
};
|
||||
|
||||
box->addRow(
|
||||
CreateGiftTransfer(box->verticalLayout(), gift, peer),
|
||||
QMargins(0, st::boxPadding.top(), 0, 0));
|
||||
|
||||
Ui::ConfirmBox(box, {
|
||||
.text = (stars > 0)
|
||||
? tr::lng_gift_transfer_sure_for(
|
||||
@@ -641,6 +651,9 @@ void ShowTransferToBox(
|
||||
.confirmed = std::move(callback),
|
||||
.confirmText = std::move(transfer),
|
||||
});
|
||||
|
||||
const auto show = controller->uiShow();
|
||||
AddTransferGiftTable(show, box->verticalLayout(), gift);
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -673,12 +686,113 @@ void ShowTransferGiftBox(
|
||||
Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
void SetThemeFromUniqueGift(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> unique) {
|
||||
class Controller final : public ChooseRecipientBoxController {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> unique)
|
||||
: ChooseRecipientBoxController({
|
||||
.session = &window->session(),
|
||||
.callback = [=](not_null<Data::Thread*> thread) {
|
||||
const auto weak = base::make_weak(window);
|
||||
const auto peer = thread->peer();
|
||||
SendPeerThemeChangeRequest(window, peer, QString(), unique);
|
||||
if (weak) window->showPeerHistory(peer);
|
||||
if (weak) window->hideLayer(anim::type::normal);
|
||||
},
|
||||
.filter = [=](not_null<Data::Thread*> thread) {
|
||||
return thread->peer()->isUser();
|
||||
},
|
||||
.moneyRestrictionError = WriteMoneyRestrictionError,
|
||||
}) {
|
||||
}
|
||||
|
||||
private:
|
||||
void prepareViewHook() override {
|
||||
ChooseRecipientBoxController::prepareViewHook();
|
||||
delegate()->peerListSetTitle(tr::lng_gift_transfer_choose());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
window->show(
|
||||
Box<PeerListBox>(
|
||||
std::make_unique<Controller>(window, std::move(unique)),
|
||||
[](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void SendPeerThemeChangeRequest(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Data::UniqueGift> &unique,
|
||||
bool locallySet) {
|
||||
const auto api = &peer->session().api();
|
||||
|
||||
api->request(MTPmessages_SetChatWallPaper(
|
||||
MTP_flags(0),
|
||||
peer->input,
|
||||
MTPInputWallPaper(),
|
||||
MTPWallPaperSettings(),
|
||||
MTPint()
|
||||
)).afterDelay(10).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
}).send();
|
||||
|
||||
api->request(MTPmessages_SetChatTheme(
|
||||
peer->input,
|
||||
(unique
|
||||
? MTP_inputChatThemeUniqueGift(MTP_string(unique->slug))
|
||||
: MTP_inputChatTheme(MTP_string(token)))
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result);
|
||||
if (!locallySet) {
|
||||
peer->updateFullForced();
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void SetPeerTheme(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Ui::ChatTheme> &theme) {
|
||||
const auto giftTheme = token.startsWith(u"gift:"_q)
|
||||
? peer->owner().cloudThemes().themeForToken(token)
|
||||
: std::optional<Data::CloudTheme>();
|
||||
|
||||
peer->setThemeToken(token);
|
||||
const auto dropWallPaper = (peer->wallPaper() != nullptr);
|
||||
if (dropWallPaper) {
|
||||
peer->setWallPaper({});
|
||||
}
|
||||
|
||||
if (theme) {
|
||||
// Remember while changes propagate through event loop.
|
||||
controller->pushLastUsedChatTheme(theme);
|
||||
}
|
||||
|
||||
SendPeerThemeChangeRequest(
|
||||
controller,
|
||||
peer,
|
||||
token,
|
||||
giftTheme ? giftTheme->unique : nullptr,
|
||||
true);
|
||||
}
|
||||
|
||||
void ShowBuyResaleGiftBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
bool forceTon,
|
||||
not_null<PeerData*> to,
|
||||
Fn<void()> closeParentBox) {
|
||||
Fn<void(bool ok)> closeParentBox) {
|
||||
show->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
struct State {
|
||||
rpl::variable<bool> ton;
|
||||
@@ -699,6 +813,7 @@ void ShowBuyResaleGiftBox(
|
||||
const auto tabs = box->addRow(
|
||||
object_ptr<Ui::SubTabs>(
|
||||
box,
|
||||
st::defaultSubTabs,
|
||||
Ui::SubTabsOptions{
|
||||
.selected = (state->ton.current()
|
||||
? u"ton"_q
|
||||
@@ -743,12 +858,12 @@ void ShowBuyResaleGiftBox(
|
||||
const auto weak = base::make_weak(box);
|
||||
const auto done = [=](Payments::CheckoutResult result) {
|
||||
if (result == Payments::CheckoutResult::Cancelled) {
|
||||
closeParentBox();
|
||||
closeParentBox(false);
|
||||
close();
|
||||
} else if (result != Payments::CheckoutResult::Paid) {
|
||||
state->sent = false;
|
||||
} else {
|
||||
closeParentBox();
|
||||
closeParentBox(true);
|
||||
close();
|
||||
}
|
||||
};
|
||||
@@ -834,3 +949,19 @@ bool ShowTransferGiftLater(
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShowActionLocked(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
const QString &slug) {
|
||||
const auto open = [=] {
|
||||
UrlClickHandler::Open(u"https://fragment.com/gift/"_q
|
||||
+ slug
|
||||
+ u"?collection=my"_q);
|
||||
};
|
||||
show->show(Ui::MakeConfirmBox({
|
||||
.text = tr::lng_gift_transfer_locked_text(),
|
||||
.confirmed = [=](Fn<void()> close) { open(); close(); },
|
||||
.confirmText = tr::lng_gift_transfer_confirm_button(),
|
||||
.title = tr::lng_gift_transfer_locked_title(),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ struct UniqueGift;
|
||||
class SavedStarGiftId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class ChatTheme;
|
||||
} // namespace Ui
|
||||
|
||||
void ShowTransferToBox(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
@@ -37,7 +41,7 @@ void ShowBuyResaleGiftBox(
|
||||
std::shared_ptr<Data::UniqueGift> gift,
|
||||
bool forceTon,
|
||||
not_null<PeerData*> to,
|
||||
Fn<void()> closeParentBox);
|
||||
Fn<void(bool ok)> closeParentBox);
|
||||
|
||||
bool ShowResaleGiftLater(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
@@ -45,3 +49,22 @@ bool ShowResaleGiftLater(
|
||||
bool ShowTransferGiftLater(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<Data::UniqueGift> gift);
|
||||
|
||||
void SetThemeFromUniqueGift(
|
||||
not_null<Window::SessionController*> window,
|
||||
std::shared_ptr<Data::UniqueGift> unique);
|
||||
void SendPeerThemeChangeRequest(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Data::UniqueGift> &unique,
|
||||
bool locallySet = false);
|
||||
void SetPeerTheme(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
const QString &token,
|
||||
const std::shared_ptr<Ui::ChatTheme> &theme);
|
||||
|
||||
void ShowActionLocked(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
const QString &slug);
|
||||
|
||||
@@ -208,7 +208,7 @@ void TranslateBox(
|
||||
box,
|
||||
CreateLoadingTextWidget(
|
||||
box,
|
||||
st::aboutLabel,
|
||||
st::aboutLabel.style,
|
||||
std::min(original->entity()->height() / lineHeight, kMaxLines),
|
||||
state->to.value() | rpl::map([=](LanguageId id) {
|
||||
return id.locale().textDirection() == Qt::RightToLeft;
|
||||
|
||||
@@ -324,7 +324,7 @@ void UsernamesBox(
|
||||
|
||||
const auto editor = box->addRow(
|
||||
object_ptr<UsernameEditor>(box, peer),
|
||||
{});
|
||||
style::margins());
|
||||
editor->setEnabled(!isBot);
|
||||
box->setFocusCallback([=] { editor->setInnerFocus(); });
|
||||
|
||||
@@ -366,7 +366,7 @@ void UsernamesBox(
|
||||
!isBot
|
||||
? [=] { box->scrollToY(0); editor->setInnerFocus(); }
|
||||
: Fn<void()>(nullptr)),
|
||||
{});
|
||||
style::margins());
|
||||
|
||||
const auto finish = [=] {
|
||||
list->save(
|
||||
|
||||
@@ -260,8 +260,10 @@ callMuteButtonLabel: FlatLabel(defaultFlatLabel) {
|
||||
}
|
||||
}
|
||||
callMuteButtonActiveInner: IconButton {
|
||||
width: 112px;
|
||||
height: 138px;
|
||||
// width: 112px;
|
||||
// height: 138px;
|
||||
width: 68px;
|
||||
height: 79px;
|
||||
}
|
||||
callMuteButtonSmallActiveInner: IconButton {
|
||||
width: 68px;
|
||||
@@ -270,11 +272,15 @@ callMuteButtonSmallActiveInner: IconButton {
|
||||
callMuteButtonActive: CallButton {
|
||||
button: callMuteButtonActiveInner;
|
||||
bg: groupCallLive1;
|
||||
bgSize: 77px;
|
||||
bgPosition: point(18px, 18px);
|
||||
outerRadius: 18px;
|
||||
// bgSize: 77px;
|
||||
// bgPosition: point(18px, 18px);
|
||||
// outerRadius: 18px;
|
||||
bgSize: 42px;
|
||||
bgPosition: point(13px, 13px);
|
||||
outerRadius: 13px;
|
||||
outerBg: callAnswerBgOuter;
|
||||
label: callMuteButtonLabel;
|
||||
//label: callMuteButtonLabel;
|
||||
label: callButtonLabel;
|
||||
}
|
||||
callMuteButton: CallMuteButton {
|
||||
active: callMuteButtonActive;
|
||||
@@ -286,10 +292,14 @@ callMuteButton: CallMuteButton {
|
||||
sublabel: FlatLabel(defaultFlatLabel) {
|
||||
textFg: groupCallMemberNotJoinedStatus;
|
||||
}
|
||||
labelsSkip: 8px;
|
||||
sublabelSkip: 14px;
|
||||
lottieSize: size(54px, 54px);
|
||||
lottieTop: 31px;
|
||||
labelsSkip: 0px;
|
||||
sublabelSkip: 0px;
|
||||
// labelsSkip: 8px;
|
||||
// sublabelSkip: 14px;
|
||||
lottieSize: size(36px, 36px);
|
||||
lottieTop: 17px;
|
||||
// lottieSize: size(54px, 54px);
|
||||
// lottieTop: 31px;
|
||||
}
|
||||
callMuteButtonSmallActive: CallButton(callMuteButtonActive) {
|
||||
button: callMuteButtonSmallActiveInner;
|
||||
@@ -928,6 +938,10 @@ groupCallHangupInner: IconButton(callButton) {
|
||||
color: groupCallLeaveBgRipple;
|
||||
}
|
||||
}
|
||||
groupCallMessageInner: IconButton(groupCallSettingsInner) {
|
||||
icon: icon {{ "calls/call_message", groupCallIconFg }};
|
||||
iconPosition: point(-1px, 16px);
|
||||
}
|
||||
groupCallSettings: CallButton(callMicrophoneMute) {
|
||||
button: groupCallSettingsInner;
|
||||
}
|
||||
@@ -943,6 +957,15 @@ groupCallVideoInnerActive: IconButton(groupCallVideoInner) {
|
||||
groupCallVideoActive: CallButton(groupCallVideo) {
|
||||
button: groupCallVideoInnerActive;
|
||||
}
|
||||
groupCallMessage: CallButton(groupCallSettings) {
|
||||
button: groupCallMessageInner;
|
||||
}
|
||||
groupCallMessageInnerActive: IconButton(groupCallMessageInner) {
|
||||
icon: icon {{ "calls/call_message", groupCallIconFg }};
|
||||
}
|
||||
groupCallMessageActive: CallButton(groupCallMessage) {
|
||||
button: groupCallMessageInnerActive;
|
||||
}
|
||||
groupCallHangup: CallButton(callHangup) {
|
||||
button: groupCallHangupInner;
|
||||
bg: groupCallLeaveBg;
|
||||
@@ -978,6 +1001,20 @@ groupCallVideoActiveSmall: CallButton(groupCallVideoSmall) {
|
||||
rippleAreaPosition: point(8px, 12px);
|
||||
}
|
||||
}
|
||||
groupCallMessageSmall: CallButton(groupCallSettingsSmall) {
|
||||
button: IconButton(groupCallMessageInner) {
|
||||
width: 60px;
|
||||
height: 68px;
|
||||
rippleAreaPosition: point(8px, 12px);
|
||||
}
|
||||
}
|
||||
groupCallMessageActiveSmall: CallButton(groupCallMessageSmall) {
|
||||
button: IconButton(groupCallMessageInnerActive) {
|
||||
width: 60px;
|
||||
height: 68px;
|
||||
rippleAreaPosition: point(8px, 12px);
|
||||
}
|
||||
}
|
||||
groupCallScreenShareSmall: CallButton(groupCallSettingsSmall) {
|
||||
button: IconButton(groupCallSettingsInner) {
|
||||
icon: icon {{ "calls/calls_present", groupCallIconFg }};
|
||||
@@ -1658,6 +1695,19 @@ groupCallInviteLink: SettingsButton(createCallInviteLink) {
|
||||
}
|
||||
groupCallInviteLinkIcon: icon {{ "info/edit/group_manage_links", mediaviewTextLinkFg }};
|
||||
|
||||
groupCallMessagesScroll: ScrollArea(defaultScrollArea) {
|
||||
barHidden: true;
|
||||
}
|
||||
groupCallMessagePadding: margins(8px, 2px, 8px, 2px);
|
||||
groupCallMessageSkip: 8px;
|
||||
groupCallMessagePalette: TextPalette(defaultTextPalette) {
|
||||
linkFg: radialFg;
|
||||
monoFg: radialFg;
|
||||
spoilerFg: radialFg;
|
||||
}
|
||||
groupCallUserpic: 20px;
|
||||
groupCallUserpicPadding: margins(2px, 2px, 4px, 2px);
|
||||
|
||||
confcallLinkMenu: IconButton(boxTitleClose) {
|
||||
icon: icon {{ "title_menu_dots", boxTitleCloseFg }};
|
||||
iconOver: icon {{ "title_menu_dots", boxTitleCloseFgOver }};
|
||||
|
||||
@@ -825,7 +825,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
box,
|
||||
object_ptr<Ui::VerticalLayout>(box)),
|
||||
{});
|
||||
style::margins());
|
||||
groupCalls->hide(anim::type::instant);
|
||||
groupCalls->toggleOn(state->groupCallsController.shownValue());
|
||||
|
||||
@@ -833,8 +833,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
groupCalls->entity(),
|
||||
tr::lng_call_box_groupcalls_subtitle());
|
||||
state->groupCallsDelegate.setContent(groupCalls->entity()->add(
|
||||
object_ptr<PeerListContent>(box, &state->groupCallsController),
|
||||
{}));
|
||||
object_ptr<PeerListContent>(box, &state->groupCallsController)));
|
||||
state->groupCallsController.setDelegate(&state->groupCallsDelegate);
|
||||
Ui::AddSkip(groupCalls->entity());
|
||||
Ui::AddDivider(groupCalls->entity());
|
||||
@@ -853,7 +852,7 @@ void ShowCallsBox(not_null<::Window::SessionController*> window) {
|
||||
|
||||
const auto content = box->addRow(
|
||||
object_ptr<PeerListContent>(box, &state->callsController),
|
||||
{});
|
||||
style::margins());
|
||||
state->callsDelegate.setContent(content);
|
||||
state->callsController.setDelegate(&state->callsDelegate);
|
||||
|
||||
|
||||
@@ -596,6 +596,10 @@ void Instance::handleUpdate(
|
||||
handleGroupCallUpdate(session, update);
|
||||
}, [&](const MTPDupdateGroupCallChainBlocks &data) {
|
||||
handleGroupCallUpdate(session, update);
|
||||
}, [&](const MTPDupdateGroupCallMessage &data) {
|
||||
handleGroupCallUpdate(session, update);
|
||||
}, [&](const MTPDupdateGroupCallEncryptedMessage &data) {
|
||||
handleGroupCallUpdate(session, update);
|
||||
}, [](const auto &) {
|
||||
Unexpected("Update type in Calls::Instance::handleUpdate.");
|
||||
});
|
||||
@@ -711,11 +715,17 @@ void Instance::handleGroupCallUpdate(
|
||||
groupCall->handlePossibleCreateOrJoinResponse(data);
|
||||
}, [&](const MTPDupdateGroupCallConnection &data) {
|
||||
groupCall->handlePossibleCreateOrJoinResponse(data);
|
||||
}, [&](const MTPDupdateGroupCallMessage &data) {
|
||||
groupCall->handleIncomingMessage(data);
|
||||
}, [&](const MTPDupdateGroupCallEncryptedMessage &data) {
|
||||
groupCall->handleIncomingMessage(data);
|
||||
}, [](const auto &) {
|
||||
});
|
||||
}
|
||||
|
||||
if (update.type() == mtpc_updateGroupCallConnection) {
|
||||
if (update.type() == mtpc_updateGroupCallConnection
|
||||
|| update.type() == mtpc_updateGroupCallMessage
|
||||
|| update.type() == mtpc_updateGroupCallEncryptedMessage) {
|
||||
return;
|
||||
}
|
||||
const auto callId = update.match([](const MTPDupdateGroupCall &data) {
|
||||
|
||||
@@ -1144,6 +1144,9 @@ void Panel::updateControlsGeometry() {
|
||||
_bodySt->muteSize,
|
||||
_bodySt->muteStroke);
|
||||
|
||||
if (_name->naturalWidth() > innerWidth) {
|
||||
_name->resizeToWidth(innerWidth);
|
||||
}
|
||||
_name->moveToLeft(
|
||||
(widget()->width() - _name->width()) / 2,
|
||||
_bodyTop + _bodySt->nameTop);
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "calls/group/calls_group_call.h"
|
||||
|
||||
#include "calls/group/calls_group_common.h"
|
||||
#include "calls/group/calls_group_messages.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "main/session/session_show.h"
|
||||
#include "main/main_app_config.h"
|
||||
@@ -597,6 +598,7 @@ GroupCall::GroupCall(
|
||||
, _peer(join.peer)
|
||||
, _history(_peer->owner().history(_peer))
|
||||
, _api(&_peer->session().mtp())
|
||||
, _messages(std::make_unique<Group::Messages>(this, &_api))
|
||||
, _joinAs(join.joinAs)
|
||||
, _possibleJoinAs(std::move(join.possibleJoinAs))
|
||||
, _joinHash(join.joinHash)
|
||||
@@ -956,6 +958,10 @@ void GroupCall::setScheduledDate(TimeId date) {
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::setMessagesEnabled(bool enabled) {
|
||||
_messagesEnabled = enabled && !_rtmp;
|
||||
}
|
||||
|
||||
void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
|
||||
_listenersHidden = real->listenersHidden();
|
||||
|
||||
@@ -964,6 +970,11 @@ void GroupCall::subscribeToReal(not_null<Data::GroupCall*> real) {
|
||||
setScheduledDate(date);
|
||||
}, _lifetime);
|
||||
|
||||
real->messagesEnabledValue(
|
||||
) | rpl::start_with_next([=](bool enabled) {
|
||||
setMessagesEnabled(enabled);
|
||||
}, _lifetime);
|
||||
|
||||
// Postpone creating video tracks, so that we know if Panel
|
||||
// supports OpenGL and we don't need ARGB32 frames at all.
|
||||
Ui::PostponeCall(this, [=] {
|
||||
@@ -1710,6 +1721,8 @@ void GroupCall::startConference() {
|
||||
return;
|
||||
}
|
||||
applyInputCall(_conferenceCall->input());
|
||||
_realChanges.fire_copy(_conferenceCall.get());
|
||||
|
||||
initialJoinRequested();
|
||||
joinDone(
|
||||
TimestampInMsFromMsgId(response.outerMsgId),
|
||||
@@ -2301,6 +2314,9 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
const auto rtmp = data.is_rtmp_stream();
|
||||
_rtmp = rtmp;
|
||||
setScheduledDate(scheduleDate);
|
||||
if (!conference()) {
|
||||
setMessagesEnabled(data.is_messages_enabled());
|
||||
}
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
chat->setGroupCall(input, scheduleDate, rtmp);
|
||||
} else if (const auto group = _peer->asChannel()) {
|
||||
@@ -2316,6 +2332,9 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
return;
|
||||
}
|
||||
setScheduledDate(data.vschedule_date().value_or_empty());
|
||||
if (!conference()) {
|
||||
setMessagesEnabled(data.is_messages_enabled());
|
||||
}
|
||||
if (const auto streamDcId = data.vstream_dc_id()) {
|
||||
_broadcastDcId = MTP::BareDcId(streamDcId->v);
|
||||
}
|
||||
@@ -2370,6 +2389,32 @@ void GroupCall::handlePossibleCreateOrJoinResponse(
|
||||
}
|
||||
}
|
||||
|
||||
void GroupCall::handleIncomingMessage(
|
||||
const MTPDupdateGroupCallMessage &data) {
|
||||
const auto id = data.vcall().match([&](const MTPDinputGroupCall &data) {
|
||||
return data.vid().v;
|
||||
}, [](const auto &) -> CallId {
|
||||
Unexpected("slug/msg in GroupCall::handleIncomingMessage");
|
||||
});
|
||||
if (id != _id || conference()) {
|
||||
return;
|
||||
}
|
||||
_messages->received(data);
|
||||
}
|
||||
|
||||
void GroupCall::handleIncomingMessage(
|
||||
const MTPDupdateGroupCallEncryptedMessage &data) {
|
||||
const auto id = data.vcall().match([&](const MTPDinputGroupCall &data) {
|
||||
return data.vid().v;
|
||||
}, [](const auto &) -> CallId {
|
||||
Unexpected("slug/msg in GroupCall::handleIncomingMessage");
|
||||
});
|
||||
if (id != _id || !conference()) {
|
||||
return;
|
||||
}
|
||||
_messages->received(data);
|
||||
}
|
||||
|
||||
void GroupCall::handlePossibleDiscarded(const MTPDgroupCallDiscarded &data) {
|
||||
if (data.vid().v == _id) {
|
||||
LOG(("Call Info: Hangup after groupCallDiscarded."));
|
||||
@@ -2995,9 +3040,7 @@ bool GroupCall::tryCreateController() {
|
||||
});
|
||||
return result;
|
||||
},
|
||||
.e2eEncryptDecrypt = (_e2eEncryptDecrypt
|
||||
? _e2eEncryptDecrypt->callback()
|
||||
: nullptr),
|
||||
.e2eEncryptDecrypt = e2eEncryptDecrypt(),
|
||||
};
|
||||
if (Logs::DebugEnabled()) {
|
||||
auto callLogFolder = cWorkingDir() + u"DebugLogs"_q;
|
||||
@@ -3050,9 +3093,7 @@ bool GroupCall::tryCreateScreencast() {
|
||||
.videoCapture = _screenCapture,
|
||||
.videoContentType = tgcalls::VideoContentType::Screencast,
|
||||
.videoCodecPreferences = lookupVideoCodecPreferences(),
|
||||
.e2eEncryptDecrypt = (_e2eEncryptDecrypt
|
||||
? _e2eEncryptDecrypt->callback()
|
||||
: nullptr),
|
||||
.e2eEncryptDecrypt = e2eEncryptDecrypt(),
|
||||
};
|
||||
|
||||
LOG(("Call Info: Creating group screen instance"));
|
||||
@@ -4095,6 +4136,17 @@ void GroupCall::setNotRequireARGB32() {
|
||||
_requireARGB32 = false;
|
||||
}
|
||||
|
||||
std::function<std::vector<uint8_t>(
|
||||
std::vector<uint8_t> const &,
|
||||
int64_t, bool,
|
||||
int32_t)> GroupCall::e2eEncryptDecrypt() const {
|
||||
return _e2eEncryptDecrypt ? _e2eEncryptDecrypt->callback() : nullptr;
|
||||
}
|
||||
|
||||
void GroupCall::sendMessage(TextWithTags message) {
|
||||
_messages->send(std::move(message));
|
||||
}
|
||||
|
||||
auto GroupCall::otherParticipantStateValue() const
|
||||
-> rpl::producer<Group::ParticipantState> {
|
||||
return _otherParticipantStateValue.events();
|
||||
|
||||
@@ -60,6 +60,7 @@ struct RejoinEvent;
|
||||
struct RtmpInfo;
|
||||
enum class VideoQuality;
|
||||
enum class Error;
|
||||
class Messages;
|
||||
} // namespace Group
|
||||
|
||||
struct InviteRequest;
|
||||
@@ -244,6 +245,9 @@ public:
|
||||
[[nodiscard]] rpl::producer<not_null<PeerData*>> joinAsValue() const {
|
||||
return _joinAs.value();
|
||||
}
|
||||
[[nodiscard]] not_null<Group::Messages*> messages() const {
|
||||
return _messages.get();
|
||||
}
|
||||
[[nodiscard]] bool showChooseJoinAs() const;
|
||||
[[nodiscard]] TimeId scheduleDate() const {
|
||||
return _scheduleDate;
|
||||
@@ -278,6 +282,9 @@ public:
|
||||
void handlePossibleCreateOrJoinResponse(const MTPDupdateGroupCall &data);
|
||||
void handlePossibleCreateOrJoinResponse(
|
||||
const MTPDupdateGroupCallConnection &data);
|
||||
void handleIncomingMessage(const MTPDupdateGroupCallMessage &data);
|
||||
void handleIncomingMessage(
|
||||
const MTPDupdateGroupCallEncryptedMessage &data);
|
||||
void changeTitle(const QString &title);
|
||||
void toggleRecording(
|
||||
bool enabled,
|
||||
@@ -415,6 +422,12 @@ public:
|
||||
[[nodiscard]] rpl::producer<bool> videoIsWorkingValue() const {
|
||||
return _videoIsWorking.value();
|
||||
}
|
||||
[[nodiscard]] bool messagesEnabled() const {
|
||||
return _messagesEnabled.current();
|
||||
}
|
||||
[[nodiscard]] rpl::producer<bool> messagesEnabledValue() const {
|
||||
return _messagesEnabled.value();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isSharingScreen() const;
|
||||
[[nodiscard]] rpl::producer<bool> isSharingScreenValue() const;
|
||||
@@ -446,6 +459,13 @@ public:
|
||||
void pushToTalk(bool pressed, crl::time delay);
|
||||
void setNotRequireARGB32();
|
||||
|
||||
[[nodiscard]] std::function<std::vector<uint8_t>(
|
||||
std::vector<uint8_t> const &,
|
||||
int64_t, bool,
|
||||
int32_t)> e2eEncryptDecrypt() const;
|
||||
void sendMessage(TextWithTags message);
|
||||
[[nodiscard]] MTPInputGroupCall inputCall() const;
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
@@ -577,6 +597,7 @@ private:
|
||||
void saveDefaultJoinAs(not_null<PeerData*> as);
|
||||
void subscribeToReal(not_null<Data::GroupCall*> real);
|
||||
void setScheduledDate(TimeId date);
|
||||
void setMessagesEnabled(bool enabled);
|
||||
void rejoinPresentation();
|
||||
void leavePresentation();
|
||||
void checkNextJoinAction();
|
||||
@@ -647,7 +668,6 @@ private:
|
||||
|
||||
[[nodiscard]] int activeVideoSendersCount() const;
|
||||
|
||||
[[nodiscard]] MTPInputGroupCall inputCall() const;
|
||||
[[nodiscard]] MTPInputGroupCall inputCallSafe() const;
|
||||
|
||||
const not_null<Delegate*> _delegate;
|
||||
@@ -667,6 +687,7 @@ private:
|
||||
base::flat_set<uint32> _unresolvedSsrcs;
|
||||
rpl::event_stream<Error> _errors;
|
||||
std::vector<Fn<void()>> _rejoinedCallbacks;
|
||||
std::unique_ptr<Group::Messages> _messages;
|
||||
bool _recordingStoppedByMe = false;
|
||||
bool _requestedVideoChannelsUpdateScheduled = false;
|
||||
|
||||
@@ -697,6 +718,7 @@ private:
|
||||
rpl::variable<bool> _canManage = false;
|
||||
rpl::variable<bool> _videoIsWorking = false;
|
||||
rpl::variable<bool> _emptyRtmp = false;
|
||||
rpl::variable<bool> _messagesEnabled = false;
|
||||
bool _initialMuteStateSent = false;
|
||||
bool _acceptFields = false;
|
||||
|
||||
|
||||
@@ -116,13 +116,12 @@ void ConferenceCallJoinConfirm(
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_confcall_join_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_join_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
const auto wrapName = [&](not_null<PeerData*> peer) {
|
||||
return rpl::single(Ui::Text::Bold(peer->shortName()));
|
||||
};
|
||||
@@ -282,7 +281,8 @@ void ShowConferenceCallLinkBox(
|
||||
MTPphone_ToggleGroupCallSettings(
|
||||
MTP_flags(Flag::f_reset_invite_hash),
|
||||
call->input(),
|
||||
MTPbool()) // join_muted
|
||||
MTPBool(), // join_muted
|
||||
MTPBool()) // messages_enabled
|
||||
).done([=](const MTPUpdates &result) {
|
||||
call->session().api().applyUpdates(result);
|
||||
ShowConferenceCallLinkBox(show, call, args);
|
||||
@@ -323,13 +323,12 @@ void ShowConferenceCallLinkBox(
|
||||
Info::BotStarRef::CreateLinkHeaderIcon(box, &call->session()),
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_confcall_link_title(),
|
||||
st.box ? st.box->title : st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_link_title(),
|
||||
st.box ? st.box->title : st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
|
||||
@@ -1042,13 +1042,12 @@ not_null<Ui::RpWidget*> CreateReActivateHeader(not_null<QWidget*> parent) {
|
||||
st::boxRowPadding + st::confcallLinkHeaderIconPadding);
|
||||
|
||||
result->add(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
tr::lng_confcall_inactive_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding);
|
||||
tr::lng_confcall_inactive_title(),
|
||||
st::boxTitle),
|
||||
st::boxRowPadding + st::confcallLinkTitlePadding,
|
||||
style::al_top);
|
||||
result->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
result,
|
||||
|
||||