Compare commits
321 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a87529b8c9 | ||
|
|
0c30bbf40a | ||
|
|
f75bfb4369 | ||
|
|
0c34ba638c | ||
|
|
4af9c801ae | ||
|
|
e06bf16980 | ||
|
|
1018745b0b | ||
|
|
47b157bf32 | ||
|
|
09547bd6d5 | ||
|
|
d17f21c10d | ||
|
|
f11339361d | ||
|
|
04b9d4bdb5 | ||
|
|
1d8d2b6251 | ||
|
|
190bf8fc5f | ||
|
|
f463d3ec6d | ||
|
|
f9d10094ac | ||
|
|
9fc87c3cb8 | ||
|
|
0d9c30423f | ||
|
|
c4bc76c022 | ||
|
|
241f5e1d26 | ||
|
|
76f0abecfd | ||
|
|
ca45fb617e | ||
|
|
dfd63e66ff | ||
|
|
d85f162bff | ||
|
|
1d4fbc64e2 | ||
|
|
9fd32fc85b | ||
|
|
f7f195eb98 | ||
|
|
12c7bd8ee1 | ||
|
|
cd5ef069c0 | ||
|
|
f90fdce422 | ||
|
|
423254f7eb | ||
|
|
c9c0d74b68 | ||
|
|
8bf6013342 | ||
|
|
c1ab1acd44 | ||
|
|
90b955534a | ||
|
|
61c7bf2f5e | ||
|
|
ee5423762a | ||
|
|
6d29dc3b36 | ||
|
|
aa10934e85 | ||
|
|
9ff1fbcf47 | ||
|
|
51c1dc20e1 | ||
|
|
3a3bf84cfc | ||
|
|
037b936613 | ||
|
|
b631d09a40 | ||
|
|
fd4f384c3b | ||
|
|
428a501bac | ||
|
|
3ca57ae50d | ||
|
|
f1eddcd584 | ||
|
|
f979df3dfe | ||
|
|
eebcdb842d | ||
|
|
048658f838 | ||
|
|
8fd17e2e8f | ||
|
|
5c13214244 | ||
|
|
12a020cd09 | ||
|
|
b98e02f326 | ||
|
|
9a521c5340 | ||
|
|
5f26e92f5c | ||
|
|
3d78c637a2 | ||
|
|
2ab2eed633 | ||
|
|
bb7018424a | ||
|
|
a770b5d4cd | ||
|
|
c92a9585e1 | ||
|
|
cedb2d31af | ||
|
|
95da2dbc34 | ||
|
|
f9b2a8d6ac | ||
|
|
a773ad7b02 | ||
|
|
32287a51f9 | ||
|
|
37a4c79c81 | ||
|
|
95ee17bd54 | ||
|
|
469c6770fb | ||
|
|
e38123cc48 | ||
|
|
e3d7bf771f | ||
|
|
1cda90c3c5 | ||
|
|
9c86f0e0a5 | ||
|
|
910f16312c | ||
|
|
639b4bdd27 | ||
|
|
4951eeac98 | ||
|
|
79106e0c01 | ||
|
|
7485f0c960 | ||
|
|
03bdd80b2f | ||
|
|
dd74f57a66 | ||
|
|
534772722e | ||
|
|
6343221d7b | ||
|
|
4929de2bfb | ||
|
|
ac8f924909 | ||
|
|
95afcbb485 | ||
|
|
229bc56cc8 | ||
|
|
4b045a602c | ||
|
|
1e3b72ab74 | ||
|
|
bc63d9fe53 | ||
|
|
d7cb8b7065 | ||
|
|
30f4d870c5 | ||
|
|
ad515c6f4a | ||
|
|
056949416d | ||
|
|
ab6d9ff73c | ||
|
|
8b766dda8e | ||
|
|
ee0f66d746 | ||
|
|
7893ad0558 | ||
|
|
b0c5a75fb9 | ||
|
|
1c313da888 | ||
|
|
ee210ea701 | ||
|
|
e0c0d79be9 | ||
|
|
cb3bad31fa | ||
|
|
917696be36 | ||
|
|
253816641c | ||
|
|
ca2692473d | ||
|
|
8000dfac01 | ||
|
|
85cca51154 | ||
|
|
87ea49e094 | ||
|
|
66bf48e21e | ||
|
|
451056d2ab | ||
|
|
791ae64a90 | ||
|
|
3b4563772e | ||
|
|
16b786186b | ||
|
|
7be1c4ca2f | ||
|
|
17fba16c23 | ||
|
|
5fc4dcd172 | ||
|
|
b27a2cd34a | ||
|
|
569340c7d3 | ||
|
|
3715fa4b1e | ||
|
|
d95e54cb1a | ||
|
|
470b67f557 | ||
|
|
c46bcef9ff | ||
|
|
c31cda0587 | ||
|
|
5758f756c9 | ||
|
|
f199205592 | ||
|
|
2b656f7745 | ||
|
|
7be286751b | ||
|
|
04617e4a12 | ||
|
|
405ccb8580 | ||
|
|
131ef4f15a | ||
|
|
07f45b7eab | ||
|
|
87addd41b1 | ||
|
|
0a4f91a53d | ||
|
|
3b76a908a4 | ||
|
|
9e3bc966c8 | ||
|
|
149c69809d | ||
|
|
97a239a8b4 | ||
|
|
bd7cee2252 | ||
|
|
66d0d6e8fe | ||
|
|
117d6192fa | ||
|
|
763bdf8798 | ||
|
|
56a82600f8 | ||
|
|
a3e993253c | ||
|
|
04d5158ae3 | ||
|
|
38e4daacd4 | ||
|
|
44d156760e | ||
|
|
d66541989e | ||
|
|
56c4d164f3 | ||
|
|
e2c1c4c8de | ||
|
|
a465117689 | ||
|
|
c2117e7722 | ||
|
|
7de28fc4bd | ||
|
|
529ef64257 | ||
|
|
9cb5423d40 | ||
|
|
dd136350fb | ||
|
|
ef7087348a | ||
|
|
8eac2dcb78 | ||
|
|
f690f93f32 | ||
|
|
77ebdd3576 | ||
|
|
3e895d0e85 | ||
|
|
385a7eb00d | ||
|
|
1c9775baf9 | ||
|
|
fb96d2eef8 | ||
|
|
debeb61540 | ||
|
|
03cdddfe18 | ||
|
|
caef7dde24 | ||
|
|
0b08810d5a | ||
|
|
694f771131 | ||
|
|
956bb876f6 | ||
|
|
99037d3d46 | ||
|
|
ea0a616453 | ||
|
|
815a18be94 | ||
|
|
3814b0833d | ||
|
|
549789bfb7 | ||
|
|
a539fad3e2 | ||
|
|
ee96d78656 | ||
|
|
288c1130b9 | ||
|
|
d1083a1fb4 | ||
|
|
e1fe373504 | ||
|
|
1a06714f3a | ||
|
|
7316d24ca4 | ||
|
|
b814c6307a | ||
|
|
06fbb2edc4 | ||
|
|
0ee47bb10f | ||
|
|
9f228d8146 | ||
|
|
de270d5283 | ||
|
|
3f041545bf | ||
|
|
1139a59818 | ||
|
|
1e64ba8f29 | ||
|
|
65342559c7 | ||
|
|
e150db1d0b | ||
|
|
4548b14b45 | ||
|
|
dac1128dc9 | ||
|
|
c3944d95b4 | ||
|
|
02b65a42f7 | ||
|
|
4869ce2247 | ||
|
|
e40267f45b | ||
|
|
e9ab8df737 | ||
|
|
9276101809 | ||
|
|
0c3773486e | ||
|
|
b831766375 | ||
|
|
325989ed47 | ||
|
|
63203ecc00 | ||
|
|
93793d8bdd | ||
|
|
6710372d27 | ||
|
|
a8c94deca8 | ||
|
|
e97d014a01 | ||
|
|
7ffa348e43 | ||
|
|
b999d87388 | ||
|
|
fb00d523c3 | ||
|
|
d7f7a03eb4 | ||
|
|
bc83df9d7f | ||
|
|
61c1c10ed9 | ||
|
|
4bad642190 | ||
|
|
f48732f813 | ||
|
|
10e28913ca | ||
|
|
230dd29af5 | ||
|
|
169db40e9f | ||
|
|
116b483a88 | ||
|
|
57cdef4e6b | ||
|
|
1b1b1780db | ||
|
|
e479daca03 | ||
|
|
51d350c356 | ||
|
|
9da4bd671e | ||
|
|
d298953653 | ||
|
|
1d52ba7a42 | ||
|
|
850940116d | ||
|
|
fd59147b8a | ||
|
|
93a52bb66e | ||
|
|
7e4c9f98a6 | ||
|
|
7d2896dd42 | ||
|
|
f7f797dd78 | ||
|
|
4b7b1c35e1 | ||
|
|
708b1d7ad4 | ||
|
|
abf49e1672 | ||
|
|
052e4bc508 | ||
|
|
80fedcbbae | ||
|
|
787b5f549a | ||
|
|
e8a28a57df | ||
|
|
b05ea9fc25 | ||
|
|
8d94cfb61b | ||
|
|
d8a58991c4 | ||
|
|
03cf8b6ac2 | ||
|
|
32e8ae2b9e | ||
|
|
27d84befa8 | ||
|
|
3bf709d459 | ||
|
|
2a8b6f05c8 | ||
|
|
f29b331470 | ||
|
|
a8b74d4b6b | ||
|
|
415d817034 | ||
|
|
3f2cc01f48 | ||
|
|
f1cd70d8a8 | ||
|
|
bacaf805b5 | ||
|
|
137fa0378c | ||
|
|
ff44094ded | ||
|
|
06982fdf04 | ||
|
|
9cf4cf6dca | ||
|
|
db2018c765 | ||
|
|
020e62fb7a | ||
|
|
2ed5552279 | ||
|
|
4d92d74de0 | ||
|
|
4fd50cfb70 | ||
|
|
27f248645c | ||
|
|
3f1a2d0b58 | ||
|
|
6fe36e6534 | ||
|
|
f36e2981ca | ||
|
|
22f210ea8e | ||
|
|
b23bfe8b02 | ||
|
|
6aa930d510 | ||
|
|
4a10a88ecf | ||
|
|
f4754f210c | ||
|
|
873b77cf40 | ||
|
|
a7d9aa947b | ||
|
|
234b0ffcf0 | ||
|
|
c50ade565a | ||
|
|
0005e0a3ce | ||
|
|
04bf24288a | ||
|
|
01d0479335 | ||
|
|
055c145af5 | ||
|
|
328b090877 | ||
|
|
1d2c86839b | ||
|
|
59574532c6 | ||
|
|
4544a2e331 | ||
|
|
c0f8ab8da0 | ||
|
|
d02617867c | ||
|
|
78c99a1583 | ||
|
|
9ec797857d | ||
|
|
b310011dee | ||
|
|
633084ed9c | ||
|
|
ab8889b2fa | ||
|
|
d563e746ab | ||
|
|
7c6ede0908 | ||
|
|
c22aeb8b40 | ||
|
|
cabe06256b | ||
|
|
2fad4e9956 | ||
|
|
c5df4db621 | ||
|
|
68b1024dd4 | ||
|
|
c894ce30c4 | ||
|
|
33fce38f90 | ||
|
|
e123399f7c | ||
|
|
0805c09446 | ||
|
|
8135f4b427 | ||
|
|
2f0331b2e0 | ||
|
|
69b6b48738 | ||
|
|
3cda267787 | ||
|
|
ffba901620 | ||
|
|
2351865961 | ||
|
|
7a11d1e31f | ||
|
|
f2b3d9714f | ||
|
|
8477956117 | ||
|
|
77b8d56c03 | ||
|
|
90fb9eccd4 | ||
|
|
c3c6571835 | ||
|
|
b9d3ba621e | ||
|
|
4757ad6c97 | ||
|
|
9c909c8992 | ||
|
|
0710dde4d5 | ||
|
|
8008d8a3d4 | ||
|
|
5c5eead0f5 | ||
|
|
696c70e34a |
@@ -170,6 +170,7 @@ buildXkbCommon() {
|
||||
git clone https://github.com/xkbcommon/libxkbcommon.git
|
||||
|
||||
cd "$EXTERNAL/libxkbcommon"
|
||||
git checkout xkbcommon-0.8.4
|
||||
./autogen.sh --prefix=$XKB_PATH
|
||||
make $MAKE_ARGS
|
||||
sudo make install
|
||||
|
||||
@@ -92,6 +92,67 @@ index bcd29b6fe1..bcb0672f69 100644
|
||||
if (QPlatformCursor *platformCursor = q->screen()->handle()->cursor()) {
|
||||
QCursor *c = QGuiApplication::overrideCursor();
|
||||
if (!c && hasCursor)
|
||||
diff --git a/src/gui/painting/qbezier.cpp b/src/gui/painting/qbezier.cpp
|
||||
index 8e0e76f787..a61bd62834 100644
|
||||
--- a/src/gui/painting/qbezier.cpp
|
||||
+++ b/src/gui/painting/qbezier.cpp
|
||||
@@ -45,6 +45,34 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
//#define QDEBUG_BEZIER
|
||||
|
||||
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
|
||||
+#ifdef Q_OS_WIN
|
||||
+Q_NEVER_INLINE void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
|
||||
+{
|
||||
+ Q_ASSERT(firstHalf);
|
||||
+ Q_ASSERT(secondHalf);
|
||||
+
|
||||
+ qreal c = (x2 + x3)*.5;
|
||||
+ firstHalf->x2 = (x1 + x2)*.5;
|
||||
+ secondHalf->x3 = (x3 + x4)*.5;
|
||||
+ firstHalf->x1 = x1;
|
||||
+ secondHalf->x4 = x4;
|
||||
+ firstHalf->x3 = (firstHalf->x2 + c)*.5;
|
||||
+ secondHalf->x2 = (secondHalf->x3 + c)*.5;
|
||||
+ firstHalf->x4 = secondHalf->x1 = (firstHalf->x3 + secondHalf->x2)*.5;
|
||||
+
|
||||
+ c = (y2 + y3)/2;
|
||||
+ firstHalf->y2 = (y1 + y2)*.5;
|
||||
+ secondHalf->y3 = (y3 + y4)*.5;
|
||||
+ firstHalf->y1 = y1;
|
||||
+ secondHalf->y4 = y4;
|
||||
+ firstHalf->y3 = (firstHalf->y2 + c)*.5;
|
||||
+ secondHalf->y2 = (secondHalf->y3 + c)*.5;
|
||||
+ firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
|
||||
+}
|
||||
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
|
||||
+#endif // Q_OS_WIN
|
||||
+
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
diff --git a/src/gui/painting/qbezier_p.h b/src/gui/painting/qbezier_p.h
|
||||
index dd1cd94acf..aedc8b6a4b 100644
|
||||
--- a/src/gui/painting/qbezier_p.h
|
||||
+++ b/src/gui/painting/qbezier_p.h
|
||||
@@ -215,6 +215,8 @@ inline QPointF QBezier::secondDerivedAt(qreal t) const
|
||||
a * y1 + b * y2 + c * y3 + d * y4);
|
||||
}
|
||||
|
||||
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
|
||||
+#ifndef Q_OS_WIN
|
||||
inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
|
||||
{
|
||||
Q_ASSERT(firstHalf);
|
||||
@@ -238,6 +240,8 @@ inline void QBezier::split(QBezier *firstHalf, QBezier *secondHalf) const
|
||||
secondHalf->y2 = (secondHalf->y3 + c)*.5;
|
||||
firstHalf->y4 = secondHalf->y1 = (firstHalf->y3 + secondHalf->y2)*.5;
|
||||
}
|
||||
+// Patch: Workaround VS2019 compiler bug, see QTBUG-75280.
|
||||
+#endif // Q_OS_WIN
|
||||
|
||||
inline void QBezier::parameterSplitLeft(qreal t, QBezier *left)
|
||||
{
|
||||
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
|
||||
index 918c98997b..4158259743 100644
|
||||
--- a/src/gui/painting/qpaintengine_p.h
|
||||
@@ -234,6 +295,40 @@ index c4cb8e65c0..45793e364f 100644
|
||||
channels[i].socket->close();
|
||||
delete channels[i].socket;
|
||||
}
|
||||
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
index 94235a48dd..9abd2cc0a1 100644
|
||||
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
@@ -2045,6 +2045,9 @@ void QNetworkReplyHttpImplPrivate::finished()
|
||||
{
|
||||
Q_Q(QNetworkReplyHttpImpl);
|
||||
|
||||
+ // Patch: Fix crash in Linux (by crashreports).
|
||||
+ QPointer<QNetworkReplyHttpImpl> guard = q;
|
||||
+
|
||||
if (state == Finished || state == Aborted || state == WaitingForSession)
|
||||
return;
|
||||
|
||||
@@ -2075,6 +2078,9 @@ void QNetworkReplyHttpImplPrivate::finished()
|
||||
#endif
|
||||
}
|
||||
|
||||
+ // Patch: Fix crash in Linux (by crashreports).
|
||||
+ if (!guard) return;
|
||||
+
|
||||
// if we don't know the total size of or we received everything save the cache
|
||||
if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
|
||||
completeCacheSave();
|
||||
@@ -2084,6 +2090,9 @@ void QNetworkReplyHttpImplPrivate::finished()
|
||||
if (isHttpRedirectResponse() && errorCode == QNetworkReply::NoError)
|
||||
return;
|
||||
|
||||
+ // Patch: Fix crash in Linux (by crashreports).
|
||||
+ if (!guard) return;
|
||||
+
|
||||
state = Finished;
|
||||
q->setFinished(true);
|
||||
|
||||
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
|
||||
index 41834b21ae..8cdf4ab145 100644
|
||||
--- a/src/network/socket/qnativesocketengine_win.cpp
|
||||
@@ -253,7 +348,7 @@ index 41834b21ae..8cdf4ab145 100644
|
||||
setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString);
|
||||
socketState = QAbstractSocket::UnconnectedState;
|
||||
diff --git a/src/platformsupport/cglconvenience/cglconvenience.mm b/src/platformsupport/cglconvenience/cglconvenience.mm
|
||||
index fb609ae485..7cca45ded4 100644
|
||||
index fb609ae485..ef1c638d91 100644
|
||||
--- a/src/platformsupport/cglconvenience/cglconvenience.mm
|
||||
+++ b/src/platformsupport/cglconvenience/cglconvenience.mm
|
||||
@@ -128,7 +128,12 @@ void *qcgl_createNSOpenGLPixelFormat(const QSurfaceFormat &format)
|
||||
|
||||
@@ -51,11 +51,6 @@ attentionButtonFgOver: #d14e4e; // default attention button text with mouse over
|
||||
attentionButtonBgOver: #fcdfde; // default attention button background with mouse over
|
||||
attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect
|
||||
|
||||
outlineButtonBg: windowBg; // default left outlined button background (like shared media links in profiles)
|
||||
outlineButtonBgOver: lightButtonBgOver; // default left outlined button background with mouse over
|
||||
outlineButtonOutlineFg: windowBgActive; // default left outlined button left outline border
|
||||
outlineButtonBgRipple: lightButtonBgRipple; // default left outlined button ripple effect
|
||||
|
||||
menuBg: windowBg; // default popup menu background
|
||||
menuBgOver: windowBgOver; // default popup menu item background with mouse over
|
||||
menuBgRipple: windowBgRipple; // default popup menu item ripple effect
|
||||
|
||||
BIN
Telegram/Resources/icons/send_control_schedule.png
Normal file
|
After Width: | Height: | Size: 743 B |
BIN
Telegram/Resources/icons/send_control_schedule@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/send_control_schedule@3x.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Telegram/Resources/icons/send_control_scheduled.png
Normal file
|
After Width: | Height: | Size: 362 B |
BIN
Telegram/Resources/icons/send_control_scheduled@2x.png
Normal file
|
After Width: | Height: | Size: 652 B |
BIN
Telegram/Resources/icons/send_control_scheduled@3x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/send_control_scheduled_dot.png
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
Telegram/Resources/icons/send_control_scheduled_dot@2x.png
Normal file
|
After Width: | Height: | Size: 406 B |
BIN
Telegram/Resources/icons/send_control_scheduled_dot@3x.png
Normal file
|
After Width: | Height: | Size: 622 B |
BIN
Telegram/Resources/icons/theme_preview.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/theme_preview@2x.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Telegram/Resources/icons/theme_preview@3x.png
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
@@ -76,6 +76,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_month_day_year" = "{month} {day}, {year}";
|
||||
"lng_month_year" = "{month} {year}";
|
||||
|
||||
"lng_calendar_beginning" = "Beginning";
|
||||
|
||||
"lng_box_ok" = "OK";
|
||||
"lng_box_done" = "Done";
|
||||
|
||||
@@ -145,6 +147,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_sure_ban_admin" = "This user is an admin. Are you sure you want to go ahead and restrict them?";
|
||||
"lng_sure_enable_socks" = "Are you sure you want to enable this proxy?\n\nServer: {server}\nPort: {port}\n\nYou can change your proxy server later in the Settings (Connection Type).";
|
||||
"lng_sure_enable" = "Enable";
|
||||
"lng_proxy_invalid" = "The proxy link is invalid.";
|
||||
"lng_proxy_unsupported" = "Your Telegram Desktop version doesn't support this proxy type or the proxy link is invalid. Please update Telegram Desktop to the latest version.";
|
||||
|
||||
"lng_edit_deleted" = "This message was deleted";
|
||||
"lng_edit_too_long" = "Your message text is too long";
|
||||
@@ -162,6 +166,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_edit_media_album_error" = "This file cannot be saved as a part of an album.";
|
||||
"lng_edit_media_invalid_file" = "Sorry, no way to use this file.";
|
||||
"lng_edit_caption_attach" = "Sorry, you can't attach a new media while you're editing your message.";
|
||||
|
||||
"lng_intro_about" = "Welcome to the official Telegram Desktop app.\nIt's fast and secure.";
|
||||
"lng_start_msgs" = "START MESSAGING";
|
||||
@@ -295,6 +300,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_notification_reply" = "Reply";
|
||||
"lng_notification_hide_all" = "Hide all";
|
||||
"lng_notification_sample" = "This is a sample notification";
|
||||
"lng_notification_reminder" = "Reminder";
|
||||
|
||||
"lng_settings_section_general" = "General";
|
||||
"lng_settings_change_lang" = "Change language";
|
||||
@@ -323,6 +329,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_replace_emojis" = "Replace emoji";
|
||||
"lng_settings_suggest_emoji" = "Suggest emoji replacements";
|
||||
"lng_settings_suggest_by_emoji" = "Suggest popular stickers by emoji";
|
||||
"lng_settings_loop_stickers" = "Loop animated stickers";
|
||||
"lng_settings_large_emoji" = "Large emoji";
|
||||
"lng_settings_view_emojis" = "View list";
|
||||
"lng_settings_send_enter" = "Send by Enter";
|
||||
"lng_settings_send_ctrlenter" = "Send by Ctrl+Enter";
|
||||
@@ -332,7 +340,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_bg_use_default" = "Use default color theme";
|
||||
"lng_settings_bg_from_gallery" = "Choose from gallery";
|
||||
"lng_settings_bg_from_file" = "Choose from file";
|
||||
"lng_settings_bg_edit_theme" = "Launch theme editor";
|
||||
"lng_settings_bg_theme_edit" = "Edit theme";
|
||||
"lng_settings_bg_theme_create" = "Create new theme";
|
||||
"lng_settings_bg_cloud_themes" = "Custom themes";
|
||||
"lng_settings_bg_show_all" = "Show all themes";
|
||||
"lng_settings_bg_tile" = "Tile background";
|
||||
"lng_settings_adaptive_wide" = "Adaptive layout for wide screens";
|
||||
|
||||
@@ -381,6 +392,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_stickers_emoji" = "Stickers and emoji";
|
||||
"lng_settings_messages" = "Messages";
|
||||
"lng_settings_themes" = "Themes";
|
||||
"lng_settings_theme_day" = "Day";
|
||||
"lng_settings_theme_classic" = "Classic";
|
||||
"lng_settings_theme_tinted" = "Tinted";
|
||||
"lng_settings_theme_night" = "Night";
|
||||
"lng_settings_theme_accent_title" = "Choose accent color";
|
||||
"lng_settings_data_storage" = "Data and storage";
|
||||
"lng_settings_information" = "Edit profile";
|
||||
"lng_settings_passcode_title" = "Local passcode";
|
||||
@@ -400,6 +416,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";
|
||||
"lng_theme_keep_changes" = "Keep changes";
|
||||
"lng_theme_revert" = "Revert";
|
||||
"lng_theme_no_desktop" = "Sorry, this theme doesn't include a version for Telegram Desktop.";
|
||||
"lng_theme_share" = "Share";
|
||||
"lng_theme_edit" = "Edit";
|
||||
"lng_theme_delete" = "Delete";
|
||||
"lng_theme_delete_sure" = "Are you sure you want to delete this theme?";
|
||||
"lng_background_header" = "Background preview";
|
||||
"lng_background_text1" = "Ah, you kids today with techno music! You should enjoy the classics, like Hasselhoff!";
|
||||
"lng_background_text2" = "I can't even take you seriously right now.";
|
||||
@@ -560,6 +581,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic.";
|
||||
|
||||
"lng_settings_blocked_users" = "Blocked users";
|
||||
"lng_settings_no_blocked_users" = "None";
|
||||
"lng_settings_show_sessions" = "Show all sessions";
|
||||
"lng_settings_export_data" = "Export Telegram data";
|
||||
"lng_settings_destroy_if" = "If away for...";
|
||||
@@ -620,6 +642,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_phone_number_always_title" = "Always share with";
|
||||
"lng_edit_privacy_phone_number_never_title" = "Never share with";
|
||||
|
||||
"lng_edit_privacy_phone_number_find" = "Who can find me by my number";
|
||||
"lng_edit_privacy_phone_number_contacts" = "Users who add your number to their contacts will see it on Telegram only if they are your contacts.";
|
||||
|
||||
"lng_edit_privacy_lastseen_title" = "Last seen privacy";
|
||||
"lng_edit_privacy_lastseen_header" = "Who can see your last seen time";
|
||||
"lng_edit_privacy_lastseen_warning" = "Important: you won't be able to see Last Seen times for people with whom you don't share your Last Seen time. Approximate last seen will be shown instead (recently, within a week, within a month).";
|
||||
@@ -1089,6 +1114,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edited" = "edited";
|
||||
"lng_edited_date" = "Edited: {date}";
|
||||
"lng_admin_badge" = "admin";
|
||||
"lng_owner_badge" = "owner";
|
||||
"lng_channel_badge" = "channel";
|
||||
"lng_fast_reply" = "Reply";
|
||||
"lng_cancel_edit_post_sure" = "Cancel editing?";
|
||||
@@ -1124,6 +1150,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_media_size_limit" = "Limit by size";
|
||||
"lng_media_size_up_to" = "up to {size}";
|
||||
"lng_media_chat_background" = "Chat background";
|
||||
"lng_media_color_theme" = "Color theme";
|
||||
|
||||
"lng_emoji_category1" = "People";
|
||||
"lng_emoji_category2" = "Nature";
|
||||
@@ -1229,6 +1256,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cant_invite_make_admin" = "Make admin";
|
||||
|
||||
"lng_send_button" = "Send";
|
||||
"lng_send_silent_message" = "Send without sound";
|
||||
"lng_schedule_message" = "Schedule message";
|
||||
"lng_reminder_message" = "Set a reminder";
|
||||
"lng_schedule_title" = "Send this message on...";
|
||||
"lng_remind_title" = "Remind me on...";
|
||||
"lng_schedule_at" = "at";
|
||||
"lng_message_ph" = "Write a message...";
|
||||
"lng_broadcast_ph" = "Broadcast a message...";
|
||||
"lng_broadcast_silent_ph" = "Silent broadcast...";
|
||||
@@ -1248,6 +1281,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_saved_short" = "Save";
|
||||
"lng_saved_forward_here" = "Forward messages here for quick access";
|
||||
|
||||
"lng_scheduled_messages" = "Scheduled Messages";
|
||||
"lng_reminder_messages" = "Reminders";
|
||||
"lng_scheduled_send_now" = "Send message now?";
|
||||
"lng_scheduled_send_now_many#one" = "Send {count} message now?";
|
||||
"lng_scheduled_send_now_many#other" = "Send {count} messages now?";
|
||||
|
||||
"lng_archived_name" = "Archived chats";
|
||||
"lng_archived_add" = "Archive";
|
||||
"lng_archived_remove" = "Unarchive";
|
||||
@@ -1375,6 +1414,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_reply_msg" = "Reply";
|
||||
"lng_context_edit_msg" = "Edit";
|
||||
"lng_context_forward_msg" = "Forward Message";
|
||||
"lng_context_send_now_msg" = "Send now";
|
||||
"lng_context_delete_msg" = "Delete Message";
|
||||
"lng_context_select_msg" = "Select Message";
|
||||
"lng_context_report_msg" = "Report Message";
|
||||
@@ -1384,6 +1424,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_copy_selected" = "Copy Selected Text";
|
||||
"lng_context_copy_selected_items" = "Copy Selected as Text";
|
||||
"lng_context_forward_selected" = "Forward Selected";
|
||||
"lng_context_send_now_selected" = "Send selected now";
|
||||
"lng_context_delete_selected" = "Delete Selected";
|
||||
"lng_context_clear_selection" = "Clear Selection";
|
||||
"lng_send_image_empty" = "Could not send an empty file: {name}";
|
||||
@@ -1461,6 +1502,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_selected_clear" = "Cancel";
|
||||
"lng_selected_delete" = "Delete";
|
||||
"lng_selected_forward" = "Forward";
|
||||
"lng_selected_send_now" = "Send now";
|
||||
"lng_selected_cancel_sure_this" = "Cancel uploading?";
|
||||
"lng_selected_upload_stop" = "Stop";
|
||||
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
|
||||
@@ -1525,6 +1567,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_preview_generating" = "Generating color theme preview...";
|
||||
"lng_theme_preview_invalid" = "Invalid data in this theme file.";
|
||||
"lng_theme_preview_apply" = "Apply this theme";
|
||||
"lng_theme_preview_users#one" = "{count} person is using this theme";
|
||||
"lng_theme_preview_users#other" = "{count} people are using this theme";
|
||||
|
||||
"lng_new_authorization" = "{name},\nWe detected a login into your account from a new device on {day}, {date} at {time}\n\nDevice: {device}\nLocation: {location}\n\nIf this wasn't you, you can go to Settings — Show all sessions and terminate that session.\n\nIf you think that somebody logged in to your account against your will, you can enable two-step verification in Settings.\n\nSincerely,\nThe Telegram Team";
|
||||
|
||||
@@ -1568,9 +1612,26 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_theme_editor_save_palette" = "Save palette file";
|
||||
"lng_theme_editor_choose_name" = "Save theme file";
|
||||
"lng_theme_editor_error" = "The editor encountered an error :( See 'log.txt' for details.";
|
||||
"lng_theme_editor_sure_close" = "Are you sure you want to close the editor? Your changes won't be saved.";
|
||||
"lng_theme_editor_need_auth" = "You need to log in to save your theme.";
|
||||
"lng_theme_editor_need_unlock" = "You need to unlock Telegram to save your theme.";
|
||||
"lng_theme_editor_done" = "Theme exported successfully!";
|
||||
"lng_theme_editor_title" = "Edit color palette";
|
||||
"lng_theme_editor_export_button" = "Export theme";
|
||||
"lng_theme_editor_save_button" = "Save theme";
|
||||
|
||||
"lng_theme_editor_create_title" = "Create theme";
|
||||
"lng_theme_editor_attach_title" = "Attach desktop theme";
|
||||
"lng_theme_editor_create" = "Create";
|
||||
"lng_theme_editor_name" = "Theme name";
|
||||
"lng_theme_editor_create_description" = "New theme will be based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
|
||||
"lng_theme_editor_attach_description" = "You can create desktop part of your theme based on your currently selected colors and wallpaper. Alternatively, you can import existing theme or color palette from file.";
|
||||
"lng_theme_editor_import_existing" = "Import existing theme";
|
||||
"lng_theme_editor_save_title" = "Save theme";
|
||||
"lng_theme_editor_link_about" = "Your theme will be updated for all users each time you change it. Anyone can install it using this link.\n\nTheme links must be longer than 5 characters and use a-z, 0-9 and underscores.";
|
||||
|
||||
"lng_theme_editor_menu_export" = "Export theme";
|
||||
"lng_theme_editor_menu_import" = "Import theme";
|
||||
"lng_theme_editor_menu_show" = "Show palette file";
|
||||
|
||||
"lng_payments_not_supported" = "Sorry, Telegram Desktop doesn't support payments yet. Please use one of our mobile apps to do this.";
|
||||
"lng_payments_receipt_label" = "Receipt";
|
||||
@@ -1625,6 +1686,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_rights_edit_admin" = "Manage permissions";
|
||||
"lng_rights_edit_admin_header" = "What can this admin do?";
|
||||
"lng_rights_edit_admin_rank_name" = "Custom title";
|
||||
"lng_rights_edit_admin_rank_about" = "A custom title that will be shown to all members instead of '{title}'.";
|
||||
"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with the same (or more limited) permissions.";
|
||||
"lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
|
||||
|
||||
@@ -1637,6 +1700,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_rights_user_restrictions" = "User restrictions";
|
||||
"lng_rights_user_restrictions_header" = "What can this member do?";
|
||||
"lng_rights_default_restrictions_header" = "What can members of this group do?";
|
||||
"lng_rights_slowmode_header" = "Slow mode";
|
||||
"lng_rights_slowmode_off" = "Off";
|
||||
"lng_rights_slowmode_seconds#one" = "{count}s";
|
||||
"lng_rights_slowmode_seconds#other" = "{count}s";
|
||||
"lng_rights_slowmode_minutes#one" = "{count}m";
|
||||
"lng_rights_slowmode_minutes#other" = "{count}m";
|
||||
"lng_rights_slowmode_hours#one" = "{count}h";
|
||||
"lng_rights_slowmode_hours#other" = "{count}h";
|
||||
"lng_rights_slowmode_about" = "Members will be able to send only one message per this interval.";
|
||||
"lng_rights_slowmode_about_interval" = "Members will be able to send only one message {interval}.";
|
||||
"lng_rights_slowmode_interval_seconds#one" = "every {count} second";
|
||||
"lng_rights_slowmode_interval_seconds#other" = "every {count} seconds";
|
||||
"lng_rights_slowmode_interval_minutes#one" = "every {count} minute";
|
||||
"lng_rights_slowmode_interval_minutes#other" = "every {count} minutes";
|
||||
|
||||
"lng_slowmode_enabled"= "Slow mode is enabled. You can send your next message in {left}.";
|
||||
"lng_slowmode_no_many" = "Slow mode is enabled. You can't send more than one message at a time.";
|
||||
"lng_slowmode_too_long" = "Sorry, this text is too long to send as one message.\n\nSlow mode is enabled. You can't send more than one message at a time.";
|
||||
|
||||
"lng_rights_channel_info" = "Change channel info";
|
||||
"lng_rights_channel_post" = "Post messages";
|
||||
@@ -1697,6 +1778,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_exceptions_list_title" = "Exceptions";
|
||||
"lng_removed_list_title" = "Removed users";
|
||||
|
||||
"lng_admin_log_slow_mode_seconds#one" = "{count} second";
|
||||
"lng_admin_log_slow_mode_seconds#other" = "{count} seconds";
|
||||
"lng_admin_log_slow_mode_minutes#one" = "{count} minute";
|
||||
"lng_admin_log_slow_mode_minutes#other" = "{count} minutes";
|
||||
|
||||
"lng_admin_log_title_all" = "All actions";
|
||||
"lng_admin_log_title_selected" = "Selected actions";
|
||||
"lng_admin_log_filter" = "Filter";
|
||||
@@ -1771,6 +1857,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_admin_log_removed_linked_channel" = "{from} removed the linked channel";
|
||||
"lng_admin_log_changed_location_chat" = "{from} changed the group location to {address}";
|
||||
"lng_admin_log_removed_location_chat" = "{from} removed the group location";
|
||||
"lng_admin_log_changed_slow_mode" = "{from} changed slow mode to {duration}";
|
||||
"lng_admin_log_removed_slow_mode" = "{from} disabled slow mode";
|
||||
"lng_admin_log_user_with_username" = "{name} ({mention})";
|
||||
"lng_admin_log_restricted_forever" = "indefinitely";
|
||||
"lng_admin_log_restricted_until" = "until {date}";
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
// Core types (no need to gen)
|
||||
|
||||
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
|
||||
|
||||
///////////////////////////////
|
||||
/////////////////// Layer cons
|
||||
///////////////////////////////
|
||||
@@ -28,115 +24,10 @@
|
||||
//invokeWithLayer18#1c900537 query:!X = X;
|
||||
//invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer
|
||||
|
||||
///////////////////////////////
|
||||
/// Authorization key creation
|
||||
///////////////////////////////
|
||||
|
||||
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
|
||||
|
||||
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
|
||||
p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
|
||||
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
|
||||
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
|
||||
|
||||
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
|
||||
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
|
||||
|
||||
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
|
||||
|
||||
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
|
||||
|
||||
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
|
||||
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
|
||||
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
|
||||
|
||||
destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
|
||||
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
|
||||
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
|
||||
|
||||
---functions---
|
||||
|
||||
req_pq#60469778 nonce:int128 = ResPQ;
|
||||
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
|
||||
|
||||
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
|
||||
|
||||
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
|
||||
|
||||
destroy_auth_key#d1435160 = DestroyAuthKeyRes;
|
||||
|
||||
///////////////////////////////
|
||||
////////////// System messages
|
||||
///////////////////////////////
|
||||
|
||||
---types---
|
||||
|
||||
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
|
||||
|
||||
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
|
||||
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
|
||||
|
||||
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
|
||||
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
|
||||
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
|
||||
|
||||
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
|
||||
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
|
||||
|
||||
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
|
||||
|
||||
//rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually
|
||||
|
||||
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
|
||||
|
||||
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
|
||||
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
|
||||
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
|
||||
|
||||
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
|
||||
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
|
||||
|
||||
pong#347773c5 msg_id:long ping_id:long = Pong;
|
||||
|
||||
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
|
||||
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
|
||||
|
||||
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
|
||||
|
||||
//message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually
|
||||
//msg_container#73f1f8dc messages:vector<message> = MessageContainer; // parsed manually
|
||||
//msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container
|
||||
//gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually
|
||||
|
||||
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
|
||||
|
||||
//ipPort ipv4:int port:int = IpPort;
|
||||
//help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
|
||||
|
||||
ipPort#d433ad73 ipv4:int port:int = IpPort;
|
||||
ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
|
||||
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
|
||||
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
|
||||
|
||||
---functions---
|
||||
|
||||
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
|
||||
|
||||
get_future_salts#b921bd04 num:int = FutureSalts;
|
||||
|
||||
ping#7abe77ec ping_id:long = Pong;
|
||||
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
|
||||
|
||||
destroy_session#e7512126 session_id:long = DestroySessionRes;
|
||||
|
||||
contest.saveDeveloperInfo#9a5f6e95 vk_id:int name:string phone_number:string age:int city:string = Bool;
|
||||
|
||||
///////////////////////////////
|
||||
///////// Main application API
|
||||
///////////////////////////////
|
||||
|
||||
---types---
|
||||
|
||||
boolFalse#bc799737 = Bool;
|
||||
boolTrue#997275b5 = Bool;
|
||||
|
||||
@@ -217,7 +108,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
|
||||
storage.fileWebp#1081464c = storage.FileType;
|
||||
|
||||
userEmpty#200250ba id:int = User;
|
||||
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||
user#938458c1 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#ecd75d8c photo_id:long photo_small:FileLocation photo_big:FileLocation dc_id:int = UserProfilePhoto;
|
||||
@@ -232,11 +123,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
|
||||
chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channel#d31a961e flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?Vector<RestrictionReason> admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
channelFull#10916653 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation pts:int = ChatFull;
|
||||
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
|
||||
channelFull#2d895c74 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true has_scheduled:flags.19?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int pts:int = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||
@@ -249,7 +140,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#475cdbd5 photo_small:FileLocation photo_big:FileLocation dc_id:int = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
|
||||
message#452c0e65 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long restriction_reason:flags.22?Vector<RestrictionReason> = Message;
|
||||
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
@@ -303,11 +194,10 @@ photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
|
||||
geoPointEmpty#1117dd5f = GeoPoint;
|
||||
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
|
||||
|
||||
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
|
||||
|
||||
auth.sentCode#38faab5f flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int terms_of_service:flags.3?help.TermsOfService = auth.SentCode;
|
||||
auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
|
||||
|
||||
auth.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
|
||||
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
|
||||
|
||||
auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorization;
|
||||
|
||||
@@ -332,7 +222,7 @@ inputReportReasonOther#e1746d0a text:string = ReportReason;
|
||||
inputReportReasonCopyright#9b89f93a = ReportReason;
|
||||
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
|
||||
|
||||
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
|
||||
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
|
||||
|
||||
contact#f911c994 user_id:int mutual:Bool = Contact;
|
||||
|
||||
@@ -454,6 +344,9 @@ updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBanne
|
||||
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
|
||||
updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
|
||||
updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
|
||||
updateNewScheduledMessage#39a51dfb message:Message = Update;
|
||||
updateDeleteScheduledMessages#90866cee peer:Peer messages:Vector<int> = Update;
|
||||
updateTheme#8216fba3 theme:Theme = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -484,7 +377,7 @@ config#330b4067 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:fla
|
||||
|
||||
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
||||
|
||||
help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
|
||||
help.appUpdate#1da7158f flags:# can_not_skip:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
|
||||
help.noAppUpdate#c45a6536 = help.AppUpdate;
|
||||
|
||||
help.inviteText#18cb9f78 message:string = help.InviteText;
|
||||
@@ -550,6 +443,7 @@ inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
|
||||
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
|
||||
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
|
||||
inputPrivacyKeyPhoneNumber#352dafa = InputPrivacyKey;
|
||||
inputPrivacyKeyAddedByPhone#d1219bdd = InputPrivacyKey;
|
||||
|
||||
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
||||
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
||||
@@ -558,6 +452,7 @@ privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
|
||||
privacyKeyForwards#69ec56a3 = PrivacyKey;
|
||||
privacyKeyProfilePhoto#96151fed = PrivacyKey;
|
||||
privacyKeyPhoneNumber#d19ae46d = PrivacyKey;
|
||||
privacyKeyAddedByPhone#42ffd42b = PrivacyKey;
|
||||
|
||||
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
||||
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
||||
@@ -601,7 +496,7 @@ messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMess
|
||||
|
||||
webPageEmpty#eb1477e8 id:long = WebPage;
|
||||
webPagePending#c586da1c id:long date:int = WebPage;
|
||||
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
|
||||
webPage#fa64e172 flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document documents:flags.11?Vector<Document> cached_page:flags.10?Page = WebPage;
|
||||
webPageNotModified#85849473 = WebPage;
|
||||
|
||||
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
|
||||
@@ -627,6 +522,7 @@ chatInvite#dfc2f58e flags:# channel:flags.0?true broadcast:flags.1?true public:f
|
||||
inputStickerSetEmpty#ffb62b95 = InputStickerSet;
|
||||
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
|
||||
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
|
||||
inputStickerSetAnimatedEmoji#28703c8 = InputStickerSet;
|
||||
|
||||
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
|
||||
|
||||
@@ -690,8 +586,8 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
|
||||
|
||||
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
|
||||
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
|
||||
channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant;
|
||||
channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant;
|
||||
channelParticipantCreator#808d15a4 flags:# user_id:int rank:flags.0?string = ChannelParticipant;
|
||||
channelParticipantAdmin#ccbebbaf flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights rank:flags.2?string = ChannelParticipant;
|
||||
channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
|
||||
|
||||
channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
|
||||
@@ -892,7 +788,7 @@ payments.paymentForm#3f56aea3 flags:# can_save_credentials:flags.2?true password
|
||||
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector<ShippingOption> = payments.ValidatedRequestedInfo;
|
||||
|
||||
payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
|
||||
payments.paymentVerficationNeeded#6b56b921 url:string = payments.PaymentResult;
|
||||
payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
|
||||
|
||||
payments.paymentReceipt#500911e1 flags:# date:int bot_id:int invoice:Invoice provider_id:int info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption currency:string total_amount:long credentials_title:string users:Vector<User> = payments.PaymentReceipt;
|
||||
|
||||
@@ -959,6 +855,7 @@ channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBa
|
||||
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionToggleSlowMode#53909779 prev_value:int new_value:int = ChannelAdminLogEventAction;
|
||||
|
||||
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
|
||||
|
||||
@@ -1134,7 +1031,7 @@ inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
|
||||
account.wallPapersNotModified#1c199183 = account.WallPapers;
|
||||
account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
|
||||
|
||||
codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings;
|
||||
codeSettings#debebe83 flags:# allow_flashcall:flags.0?true current_number:flags.1?true allow_app_hash:flags.4?true = CodeSettings;
|
||||
|
||||
wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings;
|
||||
|
||||
@@ -1170,6 +1067,17 @@ channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
|
||||
|
||||
peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
|
||||
|
||||
restrictionReason#d072acb4 platform:string reason:string text:string = RestrictionReason;
|
||||
|
||||
inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
|
||||
inputThemeSlug#f5890df1 slug:string = InputTheme;
|
||||
|
||||
themeDocumentNotModified#483d270c = Theme;
|
||||
theme#f7d90ce0 flags:# creator:flags.0?true default:flags.1?true id:long access_hash:long slug:string title:string document:flags.2?Document installs_count:int = Theme;
|
||||
|
||||
account.themesNotModified#f41eb622 = account.Themes;
|
||||
account.themes#7f676421 hash:int themes:Vector<Theme> = account.Themes;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -1181,7 +1089,7 @@ invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
|
||||
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
|
||||
|
||||
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
|
||||
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
|
||||
auth.signUp#80eee427 phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
|
||||
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
|
||||
auth.logOut#5717da40 = Bool;
|
||||
auth.resetAuthorizations#9fab0d1a = Bool;
|
||||
@@ -1251,6 +1159,13 @@ account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSet
|
||||
account.resetWallPapers#bb3b9804 = Bool;
|
||||
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
|
||||
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
|
||||
account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document;
|
||||
account.createTheme#2b7ffd7f slug:string title:string document:InputDocument = Theme;
|
||||
account.updateTheme#3b8ea202 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument = Theme;
|
||||
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool;
|
||||
account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool;
|
||||
account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
|
||||
account.getThemes#285946f8 format:string hash:int = account.Themes;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||
@@ -1285,9 +1200,9 @@ messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?t
|
||||
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
|
||||
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||
messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
|
||||
messages.sendMessage#520c3870 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
|
||||
messages.sendMedia#3491eba9 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.10?int = Updates;
|
||||
messages.forwardMessages#d9fee60e flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer schedule_date:flags.10?int = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
|
||||
messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
|
||||
@@ -1331,9 +1246,9 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
|
||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
|
||||
messages.sendInlineBotResult#220815b0 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string schedule_date:flags.10?int = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||
messages.editMessage#48f71778 flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> schedule_date:flags.15?int = Updates;
|
||||
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
@@ -1367,7 +1282,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
|
||||
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
|
||||
messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = messages.Messages;
|
||||
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
|
||||
messages.sendMultiMedia#cc0110cb flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> schedule_date:flags.10?int = Updates;
|
||||
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
||||
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
|
||||
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
|
||||
@@ -1389,6 +1304,10 @@ messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter
|
||||
messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
|
||||
messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
|
||||
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
|
||||
messages.getScheduledHistory#e2c2685b peer:InputPeer hash:int = messages.Messages;
|
||||
messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector<int> = messages.Messages;
|
||||
messages.sendScheduledMessages#bd38850a peer:InputPeer id:Vector<int> = Updates;
|
||||
messages.deleteScheduledMessages#59ae2b16 peer:InputPeer id:Vector<int> = Updates;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1400,7 +1319,7 @@ photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
|
||||
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
||||
|
||||
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
||||
upload.getFile#e3a6cfb5 location:InputFileLocation offset:int limit:int = upload.File;
|
||||
upload.getFile#b15a9afc flags:# precise:flags.0?true location:InputFileLocation offset:int limit:int = upload.File;
|
||||
upload.saveBigFilePart#de7b673d file_id:long file_part:int file_total_parts:int bytes:bytes = Bool;
|
||||
upload.getWebFile#24e6818d location:InputWebFileLocation offset:int limit:int = upload.WebFile;
|
||||
upload.getCdnFile#2000bcc3 file_token:bytes offset:int limit:int = upload.CdnFile;
|
||||
@@ -1438,7 +1357,7 @@ channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channe
|
||||
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
|
||||
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
|
||||
channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates;
|
||||
channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates;
|
||||
channels.editAdmin#d33c8902 channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights rank:string = Updates;
|
||||
channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
|
||||
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
|
||||
channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
|
||||
@@ -1461,6 +1380,7 @@ channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
|
||||
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
|
||||
channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates;
|
||||
channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool;
|
||||
channels.toggleSlowMode#edd49ef0 channel:InputChannel seconds:int = Updates;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@@ -1495,4 +1415,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
|
||||
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
|
||||
folders.deleteFolder#1c295881 folder_id:int = Updates;
|
||||
|
||||
// LAYER 103
|
||||
// LAYER 105
|
||||
114
Telegram/Resources/tl/mtproto.tl
Normal file
@@ -0,0 +1,114 @@
|
||||
// Core types (no need to gen)
|
||||
|
||||
//vector#1cb5c415 {t:Type} # [ t ] = Vector t;
|
||||
|
||||
///////////////////////////////
|
||||
/// Authorization key creation
|
||||
///////////////////////////////
|
||||
|
||||
resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector<long> = ResPQ;
|
||||
|
||||
p_q_inner_data#83c95aec pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 = P_Q_inner_data;
|
||||
p_q_inner_data_dc#a9f55f95 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int = P_Q_inner_data;
|
||||
p_q_inner_data_temp#3c6a84d4 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 expires_in:int = P_Q_inner_data;
|
||||
p_q_inner_data_temp_dc#56fddf88 pq:string p:string q:string nonce:int128 server_nonce:int128 new_nonce:int256 dc:int expires_in:int = P_Q_inner_data;
|
||||
|
||||
server_DH_params_fail#79cb045d nonce:int128 server_nonce:int128 new_nonce_hash:int128 = Server_DH_Params;
|
||||
server_DH_params_ok#d0e8075c nonce:int128 server_nonce:int128 encrypted_answer:string = Server_DH_Params;
|
||||
|
||||
server_DH_inner_data#b5890dba nonce:int128 server_nonce:int128 g:int dh_prime:string g_a:string server_time:int = Server_DH_inner_data;
|
||||
|
||||
client_DH_inner_data#6643b654 nonce:int128 server_nonce:int128 retry_id:long g_b:string = Client_DH_Inner_Data;
|
||||
|
||||
dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128 new_nonce_hash1:int128 = Set_client_DH_params_answer;
|
||||
dh_gen_retry#46dc1fb9 nonce:int128 server_nonce:int128 new_nonce_hash2:int128 = Set_client_DH_params_answer;
|
||||
dh_gen_fail#a69dae02 nonce:int128 server_nonce:int128 new_nonce_hash3:int128 = Set_client_DH_params_answer;
|
||||
|
||||
destroy_auth_key_ok#f660e1d4 = DestroyAuthKeyRes;
|
||||
destroy_auth_key_none#0a9f2259 = DestroyAuthKeyRes;
|
||||
destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
|
||||
|
||||
---functions---
|
||||
|
||||
req_pq#60469778 nonce:int128 = ResPQ;
|
||||
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
|
||||
|
||||
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params;
|
||||
|
||||
set_client_DH_params#f5045f1f nonce:int128 server_nonce:int128 encrypted_data:string = Set_client_DH_params_answer;
|
||||
|
||||
destroy_auth_key#d1435160 = DestroyAuthKeyRes;
|
||||
|
||||
///////////////////////////////
|
||||
////////////// System messages
|
||||
///////////////////////////////
|
||||
|
||||
---types---
|
||||
|
||||
msgs_ack#62d6b459 msg_ids:Vector<long> = MsgsAck;
|
||||
|
||||
bad_msg_notification#a7eff811 bad_msg_id:long bad_msg_seqno:int error_code:int = BadMsgNotification;
|
||||
bad_server_salt#edab447b bad_msg_id:long bad_msg_seqno:int error_code:int new_server_salt:long = BadMsgNotification;
|
||||
|
||||
msgs_state_req#da69fb52 msg_ids:Vector<long> = MsgsStateReq;
|
||||
msgs_state_info#04deb57d req_msg_id:long info:string = MsgsStateInfo;
|
||||
msgs_all_info#8cc0d131 msg_ids:Vector<long> info:string = MsgsAllInfo;
|
||||
|
||||
msg_detailed_info#276d3ec6 msg_id:long answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
|
||||
msg_new_detailed_info#809db6df answer_msg_id:long bytes:int status:int = MsgDetailedInfo;
|
||||
|
||||
msg_resend_req#7d861a08 msg_ids:Vector<long> = MsgResendReq;
|
||||
|
||||
//rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult; // parsed manually
|
||||
|
||||
rpc_error#2144ca19 error_code:int error_message:string = RpcError;
|
||||
|
||||
rpc_answer_unknown#5e2ad36e = RpcDropAnswer;
|
||||
rpc_answer_dropped_running#cd78e586 = RpcDropAnswer;
|
||||
rpc_answer_dropped#a43ad8b7 msg_id:long seq_no:int bytes:int = RpcDropAnswer;
|
||||
|
||||
future_salt#0949d9dc valid_since:int valid_until:int salt:long = FutureSalt;
|
||||
future_salts#ae500895 req_msg_id:long now:int salts:vector<future_salt> = FutureSalts;
|
||||
|
||||
pong#347773c5 msg_id:long ping_id:long = Pong;
|
||||
|
||||
destroy_session_ok#e22045fc session_id:long = DestroySessionRes;
|
||||
destroy_session_none#62d350c9 session_id:long = DestroySessionRes;
|
||||
|
||||
new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long = NewSession;
|
||||
|
||||
//message msg_id:long seqno:int bytes:int body:Object = Message; // parsed manually
|
||||
//msg_container#73f1f8dc messages:vector<message> = MessageContainer; // parsed manually
|
||||
//msg_copy#e06046b2 orig_message:Message = MessageCopy; // parsed manually, not used - use msg_container
|
||||
//gzip_packed#3072cfa1 packed_data:string = Object; // parsed manually
|
||||
|
||||
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
|
||||
|
||||
//ipPort ipv4:int port:int = IpPort;
|
||||
//help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
|
||||
|
||||
ipPort#d433ad73 ipv4:int port:int = IpPort;
|
||||
ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
|
||||
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
|
||||
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
|
||||
|
||||
tlsClientHello blocks:vector<TlsBlock> = TlsClientHello;
|
||||
|
||||
tlsBlockString data:string = TlsBlock;
|
||||
tlsBlockRandom length:int = TlsBlock;
|
||||
tlsBlockZero length:int = TlsBlock;
|
||||
tlsBlockDomain = TlsBlock;
|
||||
tlsBlockGrease seed:int = TlsBlock;
|
||||
tlsBlockPublicKey = TlsBlock;
|
||||
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
|
||||
|
||||
---functions---
|
||||
|
||||
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;
|
||||
|
||||
get_future_salts#b921bd04 num:int = FutureSalts;
|
||||
|
||||
ping#7abe77ec ping_id:long = Pong;
|
||||
ping_delay_disconnect#f3427b8c ping_id:long disconnect_delay:int = Pong;
|
||||
|
||||
destroy_session#e7512126 session_id:long = DestroySessionRes;
|
||||
@@ -9,10 +9,10 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.7.13.0" />
|
||||
Version="1.8.10.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
<PublisherDisplayName>Telegram FZ-LLC</PublisherDisplayName>
|
||||
<Description>Telegram Desktop official messenger</Description>
|
||||
<Logo>Assets\logo\logo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,7,13,0
|
||||
PRODUCTVERSION 1,7,13,0
|
||||
FILEVERSION 1,8,10,0
|
||||
PRODUCTVERSION 1,8,10,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -50,12 +50,12 @@ BEGIN
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.7.13.0"
|
||||
VALUE "FileVersion", "1.8.10.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.7.13.0"
|
||||
VALUE "ProductVersion", "1.8.10.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,7,13,0
|
||||
PRODUCTVERSION 1,7,13,0
|
||||
FILEVERSION 1,8,10,0
|
||||
PRODUCTVERSION 1,8,10,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -41,12 +41,12 @@ BEGIN
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "CompanyName", "Telegram FZ-LLC"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.7.13.0"
|
||||
VALUE "FileVersion", "1.8.10.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2019"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.7.13.0"
|
||||
VALUE "ProductVersion", "1.8.10.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -307,7 +307,7 @@ void updateRegistry() {
|
||||
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (const BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
|
||||
wsprintf(nameStr, L"Telegram Desktop version %s", versionStr);
|
||||
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (const BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(publisherStr, L"Telegram Messenger LLP");
|
||||
wsprintf(publisherStr, L"Telegram FZ-LLC");
|
||||
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (const BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(icongroupStr, L"Telegram Desktop");
|
||||
RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (const BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));
|
||||
|
||||
44
Telegram/SourceFiles/api/api_common.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Api {
|
||||
|
||||
struct SendOptions {
|
||||
TimeId scheduled = 0;
|
||||
bool silent = false;
|
||||
bool handleSupportSwitch = false;
|
||||
bool removeWebPageId = false;
|
||||
};
|
||||
|
||||
enum class SendType {
|
||||
Normal,
|
||||
Scheduled,
|
||||
};
|
||||
|
||||
struct SendAction {
|
||||
explicit SendAction(not_null<History*> history) : history(history) {
|
||||
}
|
||||
|
||||
not_null<History*> history;
|
||||
SendOptions options;
|
||||
MsgId replyTo = 0;
|
||||
bool clearDraft = true;
|
||||
bool generateLocal = true;
|
||||
};
|
||||
|
||||
struct MessageToSend {
|
||||
explicit MessageToSend(not_null<History*> history) : action(history) {
|
||||
}
|
||||
|
||||
SendAction action;
|
||||
TextWithTags textWithTags;
|
||||
WebPageId webPageId = 0;
|
||||
};
|
||||
|
||||
} // namespace Api
|
||||
42
Telegram/SourceFiles/api/api_hash.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Api {
|
||||
|
||||
[[nodiscard]] inline uint32 HashInit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void HashUpdate(uint32 &already, uint32 value) {
|
||||
already = (already * 20261) + uint32(value);
|
||||
}
|
||||
|
||||
inline void HashUpdate(uint32 &already, int32 value) {
|
||||
HashUpdate(already, uint32(value));
|
||||
}
|
||||
|
||||
inline void HashUpdate(uint32 &already, uint64 value) {
|
||||
HashUpdate(already, uint32(value >> 32));
|
||||
HashUpdate(already, uint32(value & 0xFFFFFFFFULL));
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int32 HashFinalize(uint32 already) {
|
||||
return int32(already & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
template <typename IntRange>
|
||||
[[nodiscard]] inline int32 CountHash(IntRange &&range) {
|
||||
auto result = HashInit();
|
||||
for (const auto value : range) {
|
||||
HashUpdate(result, value);
|
||||
}
|
||||
return HashFinalize(result);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
193
Telegram/SourceFiles/api/api_sending.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_sending.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h" // ChannelData::addsSignature.
|
||||
#include "data/data_user.h" // App::peerName(UserData*).
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h" // NewMessageFlags.
|
||||
#include "chat_helpers/message_field.h" // ConvertTextTagsToEntities.
|
||||
#include "ui/text/text_entity.h" // TextWithEntities.
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
template <typename MediaData>
|
||||
void SendExistingMedia(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<MediaData*> media,
|
||||
Fn<MTPInputMedia()> inputMedia,
|
||||
Data::FileOrigin origin) {
|
||||
const auto history = message.action.history;
|
||||
const auto peer = history->peer;
|
||||
const auto session = &history->session();
|
||||
const auto api = &session->api();
|
||||
|
||||
message.action.clearDraft = false;
|
||||
message.action.generateLocal = true;
|
||||
api->sendAction(message.action);
|
||||
|
||||
const auto newId = FullMsgId(
|
||||
peerToChannel(peer->id),
|
||||
session->data().nextLocalMessageId());
|
||||
const auto randomId = rand_value<uint64>();
|
||||
|
||||
auto flags = NewMessageFlags(peer) | MTPDmessage::Flag::f_media;
|
||||
auto clientFlags = NewMessageClientFlags();
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (message.action.replyTo) {
|
||||
flags |= MTPDmessage::Flag::f_reply_to_msg_id;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to_msg_id;
|
||||
}
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = message.action.options.silent
|
||||
|| (channelPost && session->data().notifySilentPosts(peer));
|
||||
if (channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_views;
|
||||
flags |= MTPDmessage::Flag::f_post;
|
||||
}
|
||||
if (!channelPost) {
|
||||
flags |= MTPDmessage::Flag::f_from_id;
|
||||
} else if (peer->asChannel()->addsSignature()) {
|
||||
flags |= MTPDmessage::Flag::f_post_author;
|
||||
}
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
auto messageFromId = channelPost ? 0 : session->userId();
|
||||
auto messagePostAuthor = channelPost
|
||||
? App::peerName(session->user())
|
||||
: QString();
|
||||
|
||||
auto caption = TextWithEntities{
|
||||
message.textWithTags.text,
|
||||
ConvertTextTagsToEntities(message.textWithTags.tags)
|
||||
};
|
||||
TextUtilities::Trim(caption);
|
||||
auto sentEntities = TextUtilities::EntitiesToMTP(
|
||||
caption.entities,
|
||||
TextUtilities::ConvertOption::SkipLocal);
|
||||
if (!sentEntities.v.isEmpty()) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_entities;
|
||||
}
|
||||
const auto replyTo = message.action.replyTo;
|
||||
const auto captionText = caption.text;
|
||||
|
||||
if (message.action.options.scheduled) {
|
||||
flags |= MTPDmessage::Flag::f_from_scheduled;
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_schedule_date;
|
||||
} else {
|
||||
clientFlags |= MTPDmessage_ClientFlag::f_local_history_entry;
|
||||
}
|
||||
|
||||
session->data().registerMessageRandomId(randomId, newId);
|
||||
|
||||
history->addNewLocalMessage(
|
||||
newId.msg,
|
||||
flags,
|
||||
clientFlags,
|
||||
0,
|
||||
replyTo,
|
||||
HistoryItem::NewMessageDate(message.action.options.scheduled),
|
||||
messageFromId,
|
||||
messagePostAuthor,
|
||||
media,
|
||||
caption,
|
||||
MTPReplyMarkup());
|
||||
|
||||
auto failHandler = std::make_shared<Fn<void(const RPCError&, QByteArray)>>();
|
||||
auto performRequest = [=] {
|
||||
const auto usedFileReference = media->fileReference();
|
||||
history->sendRequestId = api->request(MTPmessages_SendMedia(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
MTP_int(replyTo),
|
||||
inputMedia(),
|
||||
MTP_string(captionText),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities,
|
||||
MTP_int(message.action.options.scheduled)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
}).fail([=](const RPCError &error) {
|
||||
(*failHandler)(error, usedFileReference);
|
||||
}).afterRequest(history->sendRequestId
|
||||
).send();
|
||||
};
|
||||
*failHandler = [=](const RPCError &error, QByteArray usedFileReference) {
|
||||
if (error.code() == 400
|
||||
&& error.type().startsWith(qstr("FILE_REFERENCE_"))) {
|
||||
api->refreshFileReference(origin, [=](const auto &result) {
|
||||
if (media->fileReference() != usedFileReference) {
|
||||
performRequest();
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
api->sendMessageFail(error, peer, randomId, newId);
|
||||
}
|
||||
};
|
||||
performRequest();
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
main->finishForwarding(message.action);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SendExistingDocument(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<DocumentData*> document) {
|
||||
const auto inputMedia = [=] {
|
||||
return MTP_inputMediaDocument(
|
||||
MTP_flags(0),
|
||||
document->mtpInput(),
|
||||
MTPint());
|
||||
};
|
||||
SendExistingMedia(
|
||||
std::move(message),
|
||||
document,
|
||||
inputMedia,
|
||||
document->stickerOrGifOrigin());
|
||||
|
||||
if (document->sticker()) {
|
||||
if (const auto main = App::main()) {
|
||||
main->incrementSticker(document);
|
||||
document->session().data().notifyRecentStickersUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendExistingPhoto(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<PhotoData*> photo) {
|
||||
const auto inputMedia = [=] {
|
||||
return MTP_inputMediaPhoto(
|
||||
MTP_flags(0),
|
||||
photo->mtpInput(),
|
||||
MTPint());
|
||||
};
|
||||
SendExistingMedia(
|
||||
std::move(message),
|
||||
photo,
|
||||
inputMedia,
|
||||
Data::FileOrigin());
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
25
Telegram/SourceFiles/api/api_sending.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class History;
|
||||
class DocumentData;
|
||||
|
||||
namespace Api {
|
||||
|
||||
struct MessageToSend;
|
||||
|
||||
void SendExistingDocument(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<DocumentData*> document);
|
||||
|
||||
void SendExistingPhoto(
|
||||
Api::MessageToSend &&message,
|
||||
not_null<PhotoData*> photo);
|
||||
|
||||
} // namespace Api
|
||||
235
Telegram/SourceFiles/api/api_single_message_search.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_single_message_search.h"
|
||||
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_search_controller.h"
|
||||
#include "core/local_url_handlers.h"
|
||||
#include "history/history_item.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
using Key = details::SingleMessageSearchKey;
|
||||
|
||||
Key ExtractKey(const QString &query) {
|
||||
const auto trimmed = query.trimmed();
|
||||
const auto local = Core::TryConvertUrlToLocal(trimmed);
|
||||
const auto check = local.isEmpty() ? trimmed : local;
|
||||
const auto parse = [&] {
|
||||
const auto delimeter = check.indexOf('?');
|
||||
return (delimeter > 0)
|
||||
? qthelp::url_parse_params(
|
||||
check.mid(delimeter + 1),
|
||||
qthelp::UrlParamNameTransform::ToLower)
|
||||
: QMap<QString, QString>();
|
||||
};
|
||||
if (check.startsWith(qstr("tg://privatepost"), Qt::CaseInsensitive)) {
|
||||
const auto params = parse();
|
||||
const auto channel = params.value("channel");
|
||||
const auto post = params.value("post").toInt();
|
||||
return (channel.toInt() && post) ? Key{ channel, post } : Key();
|
||||
} else if (check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
|
||||
const auto params = parse();
|
||||
const auto domain = params.value("domain");
|
||||
const auto post = params.value("post").toInt();
|
||||
return (!domain.isEmpty() && post) ? Key{ domain, post } : Key();
|
||||
}
|
||||
return Key();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SingleMessageSearch::SingleMessageSearch(not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
SingleMessageSearch::~SingleMessageSearch() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void SingleMessageSearch::clear() {
|
||||
_cache.clear();
|
||||
_requestKey = Key();
|
||||
_session->api().request(base::take(_requestId)).cancel();
|
||||
}
|
||||
|
||||
std::optional<HistoryItem*> SingleMessageSearch::lookup(
|
||||
const QString &query,
|
||||
Fn<void()> ready) {
|
||||
const auto key = ExtractKey(query);
|
||||
if (!key) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto i = _cache.find(key);
|
||||
if (i != end(_cache)) {
|
||||
return _session->data().message(i->second);
|
||||
}
|
||||
if (!(_requestKey == key)) {
|
||||
_session->api().request(base::take(_requestId)).cancel();
|
||||
_requestKey = key;
|
||||
}
|
||||
return performLookup(ready);
|
||||
}
|
||||
|
||||
std::optional<HistoryItem*> SingleMessageSearch::performLookupByChannel(
|
||||
not_null<ChannelData*> channel,
|
||||
Fn<void()> ready) {
|
||||
Expects(!_requestKey.empty());
|
||||
|
||||
const auto postId = _requestKey.postId;
|
||||
if (const auto item = _session->data().message(channel, postId)) {
|
||||
_cache.emplace(_requestKey, item->fullId());
|
||||
return item;
|
||||
} else if (!ready) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto fail = [=] {
|
||||
_cache.emplace(_requestKey, FullMsgId());
|
||||
ready();
|
||||
};
|
||||
_requestId = _session->api().request(MTPchannels_GetMessages(
|
||||
channel->inputChannel,
|
||||
MTP_vector<MTPInputMessage>(1, MTP_inputMessageID(MTP_int(postId)))
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
const auto received = Api::ParseSearchResult(
|
||||
channel,
|
||||
Storage::SharedMediaType::kCount,
|
||||
postId,
|
||||
Data::LoadDirection::Around,
|
||||
result);
|
||||
if (!received.messageIds.empty()
|
||||
&& received.messageIds.front() == postId) {
|
||||
_cache.emplace(
|
||||
_requestKey,
|
||||
FullMsgId(channel->bareId(), postId));
|
||||
ready();
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
fail();
|
||||
}).send();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<HistoryItem*> SingleMessageSearch::performLookupById(
|
||||
ChannelId channelId,
|
||||
Fn<void()> ready) {
|
||||
Expects(!_requestKey.empty());
|
||||
|
||||
if (const auto channel = _session->data().channelLoaded(channelId)) {
|
||||
return performLookupByChannel(channel, ready);
|
||||
} else if (!ready) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto fail = [=] {
|
||||
_cache.emplace(_requestKey, FullMsgId());
|
||||
ready();
|
||||
};
|
||||
_requestId = _session->api().request(MTPchannels_GetChannels(
|
||||
MTP_vector<MTPInputChannel>(
|
||||
1,
|
||||
MTP_inputChannel(MTP_int(channelId), MTP_long(0)))
|
||||
)).done([=](const MTPmessages_Chats &result) {
|
||||
result.match([&](const auto &data) {
|
||||
const auto peer = _session->data().processChats(data.vchats());
|
||||
if (peer && peer->id == peerFromChannel(channelId)) {
|
||||
if (performLookupByChannel(peer->asChannel(), ready)) {
|
||||
ready();
|
||||
}
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
fail();
|
||||
}).send();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<HistoryItem*> SingleMessageSearch::performLookupByUsername(
|
||||
const QString &username,
|
||||
Fn<void()> ready) {
|
||||
Expects(!_requestKey.empty());
|
||||
|
||||
if (const auto peer = _session->data().peerByUsername(username)) {
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
return performLookupByChannel(channel, ready);
|
||||
}
|
||||
_cache.emplace(_requestKey, FullMsgId());
|
||||
return nullptr;
|
||||
} else if (!ready) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto fail = [=] {
|
||||
_cache.emplace(_requestKey, FullMsgId());
|
||||
ready();
|
||||
};
|
||||
_requestId = _session->api().request(MTPcontacts_ResolveUsername(
|
||||
MTP_string(username)
|
||||
)).done([=](const MTPcontacts_ResolvedPeer &result) {
|
||||
result.match([&](const MTPDcontacts_resolvedPeer &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
_session->data().processChats(data.vchats());
|
||||
const auto peerId = peerFromMTP(data.vpeer());
|
||||
const auto peer = peerId
|
||||
? _session->data().peerLoaded(peerId)
|
||||
: nullptr;
|
||||
if (const auto channel = peer ? peer->asChannel() : nullptr) {
|
||||
if (performLookupByChannel(channel, ready)) {
|
||||
ready();
|
||||
}
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}).fail([=](const RPCError &error) {
|
||||
fail();
|
||||
}).send();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<HistoryItem*> SingleMessageSearch::performLookup(
|
||||
Fn<void()> ready) {
|
||||
Expects(!_requestKey.empty());
|
||||
|
||||
if (!_requestKey.domainOrId[0].isDigit()) {
|
||||
return performLookupByUsername(_requestKey.domainOrId, ready);
|
||||
}
|
||||
const auto channelId = _requestKey.domainOrId.toInt();
|
||||
return performLookupById(channelId, ready);
|
||||
}
|
||||
|
||||
QString ConvertPeerSearchQuery(const QString &query) {
|
||||
const auto trimmed = query.trimmed();
|
||||
const auto local = Core::TryConvertUrlToLocal(trimmed);
|
||||
const auto check = local.isEmpty() ? trimmed : local;
|
||||
if (!check.startsWith(qstr("tg://resolve"), Qt::CaseInsensitive)) {
|
||||
return query;
|
||||
}
|
||||
const auto delimeter = check.indexOf('?');
|
||||
const auto params = (delimeter > 0)
|
||||
? qthelp::url_parse_params(
|
||||
check.mid(delimeter + 1),
|
||||
qthelp::UrlParamNameTransform::ToLower)
|
||||
: QMap<QString, QString>();
|
||||
return params.value("domain", query);
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
76
Telegram/SourceFiles/api/api_single_message_search.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Api {
|
||||
namespace details {
|
||||
|
||||
struct SingleMessageSearchKey {
|
||||
QString domainOrId;
|
||||
MsgId postId = 0;
|
||||
|
||||
[[nodiscard]] bool empty() const {
|
||||
return domainOrId.isEmpty() || !postId;
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
[[nodiscard]] bool operator<(const SingleMessageSearchKey &other) const {
|
||||
return std::tie(domainOrId, postId)
|
||||
< std::tie(other.domainOrId, other.postId);
|
||||
}
|
||||
[[nodiscard]] bool operator==(
|
||||
const SingleMessageSearchKey &other) const {
|
||||
return std::tie(domainOrId, postId)
|
||||
== std::tie(other.domainOrId, other.postId);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
class SingleMessageSearch {
|
||||
public:
|
||||
explicit SingleMessageSearch(not_null<Main::Session*> session);
|
||||
~SingleMessageSearch();
|
||||
|
||||
void clear();
|
||||
|
||||
// If 'ready' callback is empty, the result must not be 'nullopt'.
|
||||
[[nodiscard]] std::optional<HistoryItem*> lookup(
|
||||
const QString &query,
|
||||
Fn<void()> ready = nullptr);
|
||||
|
||||
private:
|
||||
using Key = details::SingleMessageSearchKey;
|
||||
|
||||
[[nodiscard]] std::optional<HistoryItem*> performLookup(
|
||||
Fn<void()> ready);
|
||||
[[nodiscard]] std::optional<HistoryItem*> performLookupById(
|
||||
ChannelId channelId,
|
||||
Fn<void()> ready);
|
||||
[[nodiscard]] std::optional<HistoryItem*> performLookupByUsername(
|
||||
const QString &username,
|
||||
Fn<void()> ready);
|
||||
[[nodiscard]] std::optional<HistoryItem*> performLookupByChannel(
|
||||
not_null<ChannelData*> channel,
|
||||
Fn<void()> ready);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
std::map<Key, FullMsgId> _cache;
|
||||
mtpRequestId _requestId = 0;
|
||||
Key _requestKey;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] QString ConvertPeerSearchQuery(const QString &query);
|
||||
|
||||
} // namespace Api
|
||||
@@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include "api/api_common.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
@@ -16,13 +16,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_messages.h"
|
||||
|
||||
class TaskQueue;
|
||||
class AuthSession;
|
||||
struct MessageGroupId;
|
||||
struct SendingAlbum;
|
||||
enum class SendMediaType;
|
||||
struct FileLoadTo;
|
||||
class mtpFileLoader;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
struct UpdatedFileReferences;
|
||||
class WallPaper;
|
||||
@@ -46,23 +49,51 @@ struct CloudPasswordState;
|
||||
} // namespace Core
|
||||
|
||||
namespace Api {
|
||||
namespace details {
|
||||
|
||||
template <typename IntRange>
|
||||
inline int32 CountHash(IntRange &&range) {
|
||||
uint32 acc = 0;
|
||||
for (auto value : range) {
|
||||
acc += (acc * 20261) + uint32(value);
|
||||
inline QString ToString(const QString &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
inline QString ToString(int32 value) {
|
||||
return QString::number(value);
|
||||
}
|
||||
|
||||
inline QString ToString(uint64 value) {
|
||||
return QString::number(value);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <
|
||||
typename ...Types,
|
||||
typename = std::enable_if_t<(sizeof...(Types) > 0)>>
|
||||
QString RequestKey(Types &&...values) {
|
||||
const auto strings = { details::ToString(values)... };
|
||||
if (strings.size() == 1) {
|
||||
return *strings.begin();
|
||||
}
|
||||
return int32(acc & 0x7FFFFFFF);
|
||||
|
||||
auto result = QString();
|
||||
result.reserve(
|
||||
ranges::accumulate(strings, 0, ranges::plus(), &QString::size));
|
||||
for (const auto &string : strings) {
|
||||
result.append(string);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
class ApiWrap : public MTP::Sender, private base::Subscriber {
|
||||
public:
|
||||
using SendAction = Api::SendAction;
|
||||
using MessageToSend = Api::MessageToSend;
|
||||
|
||||
struct Privacy {
|
||||
enum class Key {
|
||||
PhoneNumber,
|
||||
AddedByPhone,
|
||||
LastSeen,
|
||||
Calls,
|
||||
Invites,
|
||||
@@ -99,11 +130,17 @@ public:
|
||||
bool operator!=(const BlockedUsersSlice &other) const;
|
||||
};
|
||||
|
||||
explicit ApiWrap(not_null<AuthSession*> session);
|
||||
explicit ApiWrap(not_null<Main::Session*> session);
|
||||
|
||||
AuthSession &session() const;
|
||||
Main::Session &session() const;
|
||||
|
||||
void applyUpdates(
|
||||
const MTPUpdates &updates,
|
||||
uint64 sentMessageRandomId = 0);
|
||||
|
||||
void registerModifyRequest(const QString &key, mtpRequestId requestId);
|
||||
void clearModifyRequest(const QString &key);
|
||||
|
||||
void applyUpdates(const MTPUpdates &updates, uint64 sentMessageRandomId = 0);
|
||||
void applyNotifySettings(
|
||||
MTPInputNotifyPeer peer,
|
||||
const MTPPeerNotifySettings &settings);
|
||||
@@ -214,10 +251,6 @@ public:
|
||||
void deleteAllFromUser(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> from);
|
||||
void saveDefaultRestrictions(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPChatBannedRights &rights,
|
||||
Fn<void(bool)> callback = nullptr);
|
||||
|
||||
void requestWebPageDelayed(WebPageData *page);
|
||||
void clearWebPageRequest(WebPageData *page);
|
||||
@@ -337,30 +370,21 @@ public:
|
||||
not_null<PeerData*> peer,
|
||||
const std::vector<not_null<UserData*>> &users);
|
||||
|
||||
struct SendOptions {
|
||||
SendOptions(not_null<History*> history);
|
||||
|
||||
not_null<History*> history;
|
||||
MsgId replyTo = 0;
|
||||
WebPageId webPageId = 0;
|
||||
bool clearDraft = false;
|
||||
bool generateLocal = true;
|
||||
bool handleSupportSwitch = false;
|
||||
};
|
||||
rpl::producer<SendOptions> sendActions() const {
|
||||
rpl::producer<SendAction> sendActions() const {
|
||||
return _sendActions.events();
|
||||
}
|
||||
void sendAction(const SendOptions &options);
|
||||
void sendAction(const SendAction &action);
|
||||
void forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const SendOptions &options);
|
||||
void shareContact(not_null<UserData*> user, const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void shareContact(not_null<UserData*> user, const SendAction &action);
|
||||
void readServerHistory(not_null<History*> history);
|
||||
void readServerHistoryForce(not_null<History*> history);
|
||||
//void readFeed( // #feed
|
||||
@@ -371,66 +395,54 @@ public:
|
||||
QByteArray result,
|
||||
VoiceWaveform waveform,
|
||||
int duration,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void sendFiles(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
std::shared_ptr<SendingAlbum> album,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
void sendFile(
|
||||
const QByteArray &fileContent,
|
||||
SendMediaType type,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
|
||||
void editMedia(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
TextWithTags &&caption,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
MsgId msgIdToEdit);
|
||||
|
||||
void sendUploadedPhoto(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
bool silent);
|
||||
Api::SendOptions options);
|
||||
void sendUploadedDocument(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent);
|
||||
Api::SendOptions options);
|
||||
void editUploadedFile(
|
||||
FullMsgId localId,
|
||||
const MTPInputFile &file,
|
||||
const std::optional<MTPInputFile> &thumb,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
bool isDocument);
|
||||
|
||||
void cancelLocalItem(not_null<HistoryItem*> item);
|
||||
|
||||
struct MessageToSend {
|
||||
MessageToSend(not_null<History*> history);
|
||||
|
||||
not_null<History*> history;
|
||||
TextWithTags textWithTags;
|
||||
MsgId replyTo = 0;
|
||||
WebPageId webPageId = 0;
|
||||
bool clearDraft = true;
|
||||
bool handleSupportSwitch = false;
|
||||
};
|
||||
void sendMessage(MessageToSend &&message);
|
||||
void sendBotStart(not_null<UserData*> bot, PeerData *chat = nullptr);
|
||||
void sendInlineResult(
|
||||
not_null<UserData*> bot,
|
||||
not_null<InlineBots::Result*> data,
|
||||
const SendOptions &options);
|
||||
void sendExistingDocument(
|
||||
not_null<DocumentData*> document,
|
||||
Data::FileOrigin origin,
|
||||
TextWithEntities caption,
|
||||
const SendOptions &options);
|
||||
|
||||
void requestSupportContact(FnMut<void(const MTPUser&)> callback);
|
||||
const SendAction &action);
|
||||
void sendMessageFail(
|
||||
const RPCError &error,
|
||||
not_null<PeerData*> peer,
|
||||
uint64 randomId = 0,
|
||||
FullMsgId itemId = FullMsgId());
|
||||
|
||||
void uploadPeerPhoto(not_null<PeerData*> peer, QImage &&image);
|
||||
void clearPeerPhoto(not_null<PhotoData*> photo);
|
||||
@@ -459,7 +471,7 @@ public:
|
||||
|
||||
void createPoll(
|
||||
const PollData &data,
|
||||
const SendOptions &options,
|
||||
const SendAction &action,
|
||||
FnMut<void()> done,
|
||||
FnMut<void(const RPCError &error)> fail);
|
||||
void sendPollVotes(
|
||||
@@ -536,10 +548,6 @@ private:
|
||||
not_null<ChannelData*> channel,
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list);
|
||||
void applyAdminsList(
|
||||
not_null<ChannelData*> channel,
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list);
|
||||
void resolveWebPages();
|
||||
void gotWebPages(
|
||||
ChannelData *channel,
|
||||
@@ -608,7 +616,7 @@ private:
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
UserId userId,
|
||||
const SendOptions &options);
|
||||
const SendAction &action);
|
||||
|
||||
void deleteHistory(
|
||||
not_null<PeerData*> peer,
|
||||
@@ -627,7 +635,6 @@ private:
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> from);
|
||||
|
||||
void sendMessageFail(const RPCError &error);
|
||||
void uploadAlbumMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const MessageGroupId &groupId,
|
||||
@@ -643,13 +650,13 @@ private:
|
||||
void sendMedia(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
bool silent);
|
||||
Api::SendOptions options);
|
||||
void sendMediaWithRandomId(
|
||||
not_null<HistoryItem*> item,
|
||||
const MTPInputMedia &media,
|
||||
bool silent,
|
||||
Api::SendOptions options,
|
||||
uint64 randomId);
|
||||
FileLoadTo fileLoadTaskOptions(const SendOptions &options) const;
|
||||
FileLoadTo fileLoadTaskOptions(const SendAction &action) const;
|
||||
|
||||
//void readFeeds(); // #feed
|
||||
|
||||
@@ -681,7 +688,9 @@ private:
|
||||
|
||||
void sendDialogRequests();
|
||||
|
||||
not_null<AuthSession*> _session;
|
||||
not_null<Main::Session*> _session;
|
||||
|
||||
base::flat_map<QString, int> _modifyRequests;
|
||||
|
||||
MessageDataRequests _messageDataRequests;
|
||||
QMap<ChannelData*, MessageDataRequests> _channelMessageDataRequests;
|
||||
@@ -710,10 +719,6 @@ private:
|
||||
not_null<UserData*>>;
|
||||
base::flat_map<KickRequest, mtpRequestId> _kickRequests;
|
||||
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
mtpRequestId> _defaultRestrictionsRequests;
|
||||
|
||||
base::flat_set<not_null<ChannelData*>> _selfParticipantRequests;
|
||||
|
||||
base::flat_map<
|
||||
@@ -794,7 +799,7 @@ private:
|
||||
not_null<Data::Folder*>,
|
||||
DialogsLoadState> _foldersLoadState;
|
||||
|
||||
rpl::event_stream<SendOptions> _sendActions;
|
||||
rpl::event_stream<SendAction> _sendActions;
|
||||
|
||||
struct ReadRequest {
|
||||
ReadRequest(mtpRequestId requestId, MsgId upTo)
|
||||
|
||||
@@ -40,13 +40,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "numbers.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_overview.h"
|
||||
#include "styles/style_mediaview.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtGui/QFontDatabase>
|
||||
|
||||
#ifdef OS_MAC_OLD
|
||||
#include <libexif/exif-data.h>
|
||||
#endif // OS_MAC_OLD
|
||||
@@ -115,33 +118,6 @@ namespace App {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void addSavedGif(DocumentData *doc) {
|
||||
auto &saved = Auth().data().savedGifsRef();
|
||||
int32 index = saved.indexOf(doc);
|
||||
if (index) {
|
||||
if (index > 0) saved.remove(index);
|
||||
saved.push_front(doc);
|
||||
if (saved.size() > Global::SavedGifsLimit()) saved.pop_back();
|
||||
Local::writeSavedGifs();
|
||||
|
||||
Auth().data().notifySavedGifsUpdated();
|
||||
Auth().data().setLastSavedGifsUpdate(0);
|
||||
Auth().api().updateStickers();
|
||||
}
|
||||
}
|
||||
|
||||
void checkSavedGif(HistoryItem *item) {
|
||||
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == Auth().user())) {
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto document = media->document()) {
|
||||
if (document->isGifv()) {
|
||||
addSavedGif(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString peerName(const PeerData *peer, bool forDialogs) {
|
||||
return peer ? ((forDialogs && peer->isUser() && !peer->asUser()->nameOrPhone.isEmpty()) ? peer->asUser()->nameOrPhone : peer->name) : tr::lng_deleted(tr::now);
|
||||
}
|
||||
@@ -341,9 +317,8 @@ namespace App {
|
||||
void quit() {
|
||||
if (quitting()) {
|
||||
return;
|
||||
} else if (AuthSession::Exists()
|
||||
&& Auth().data().exportInProgress()) {
|
||||
Auth().data().stopExportWithConfirmation([] { App::quit(); });
|
||||
} else if (Core::IsAppLaunched()
|
||||
&& Core::App().exportPreventsQuit()) {
|
||||
return;
|
||||
}
|
||||
setLaunchState(QuitRequested);
|
||||
|
||||
@@ -65,8 +65,6 @@ namespace App {
|
||||
|
||||
QString formatPhone(QString phone);
|
||||
|
||||
void addSavedGif(DocumentData *doc);
|
||||
void checkSavedGif(HistoryItem *item);
|
||||
[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
|
||||
|
||||
void hoveredItem(HistoryView::Element *item);
|
||||
|
||||
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QLatin1String>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Type>
|
||||
|
||||
42
Telegram/SourceFiles/base/invoke_queued.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class InvokeQueuedEvent : public QEvent {
|
||||
public:
|
||||
static constexpr auto kType = QEvent::Type(60666);
|
||||
|
||||
explicit InvokeQueuedEvent(FnMut<void()> &&method)
|
||||
: QEvent(kType)
|
||||
, _method(std::move(method)) {
|
||||
}
|
||||
|
||||
void invoke() {
|
||||
_method();
|
||||
}
|
||||
|
||||
private:
|
||||
FnMut<void()> _method;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
template <typename Lambda>
|
||||
inline void InvokeQueued(const QObject *context, Lambda &&lambda) {
|
||||
QCoreApplication::postEvent(
|
||||
const_cast<QObject*>(context),
|
||||
new base::InvokeQueuedEvent(std::forward<Lambda>(lambda)));
|
||||
}
|
||||
@@ -19,6 +19,7 @@ extern "C" {
|
||||
#include <openssl/modes.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
} // extern "C"
|
||||
|
||||
#ifdef small
|
||||
@@ -56,19 +57,38 @@ private:
|
||||
|
||||
class BigNum {
|
||||
public:
|
||||
BigNum() : _data(BN_new()) {
|
||||
BigNum() = default;
|
||||
BigNum(const BigNum &other)
|
||||
: _data((other.failed() || other.isZero())
|
||||
? nullptr
|
||||
: BN_dup(other.raw()))
|
||||
, _failed(other._failed) {
|
||||
}
|
||||
BigNum(const BigNum &other) : BigNum() {
|
||||
*this = other;
|
||||
BigNum(BigNum &&other)
|
||||
: _data(std::exchange(other._data, nullptr))
|
||||
, _failed(std::exchange(other._failed, false)) {
|
||||
}
|
||||
BigNum &operator=(const BigNum &other) {
|
||||
if (other.failed() || !BN_copy(raw(), other.raw())) {
|
||||
if (other.failed()) {
|
||||
_failed = true;
|
||||
} else if (other.isZero()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else if (!_data) {
|
||||
_data = BN_dup(other.raw());
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_copy(raw(), other.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &operator=(BigNum &&other) {
|
||||
std::swap(_data, other._data);
|
||||
std::swap(_failed, other._failed);
|
||||
return *this;
|
||||
}
|
||||
~BigNum() {
|
||||
BN_clear_free(raw());
|
||||
clear();
|
||||
}
|
||||
|
||||
explicit BigNum(unsigned int word) : BigNum() {
|
||||
@@ -78,64 +98,74 @@ public:
|
||||
setBytes(bytes);
|
||||
}
|
||||
|
||||
void setWord(unsigned int word) {
|
||||
if (!BN_set_word(raw(), word)) {
|
||||
_failed = true;
|
||||
BigNum &setWord(unsigned int word) {
|
||||
if (!word) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_set_word(raw(), word);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setBytes(bytes::const_span bytes) {
|
||||
if (!BN_bin2bn(
|
||||
BigNum &setBytes(bytes::const_span bytes) {
|
||||
if (bytes.empty()) {
|
||||
clear();
|
||||
_failed = false;
|
||||
} else {
|
||||
_failed = !BN_bin2bn(
|
||||
reinterpret_cast<const unsigned char*>(bytes.data()),
|
||||
bytes.size(),
|
||||
raw())) {
|
||||
_failed = true;
|
||||
raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void setAdd(const BigNum &a, const BigNum &b) {
|
||||
BigNum &setAdd(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (!BN_add(raw(), a.raw(), b.raw())) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_add(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setSub(const BigNum &a, const BigNum &b) {
|
||||
BigNum &setSub(const BigNum &a, const BigNum &b) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (!BN_sub(raw(), a.raw(), b.raw())) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_sub(raw(), a.raw(), b.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setSubWord(unsigned int word) {
|
||||
if (failed()) {
|
||||
return;
|
||||
} else if (!BN_sub_word(raw(), word)) {
|
||||
_failed = true;
|
||||
}
|
||||
}
|
||||
void setMul(
|
||||
BigNum &setMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mul(raw(), a.raw(), b.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = !BN_mul(raw(), a.raw(), b.raw(), context.raw());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BN_ULONG setDivWord(BN_ULONG word) {
|
||||
Expects(word != 0);
|
||||
if (failed()) {
|
||||
return (BN_ULONG)-1;
|
||||
}
|
||||
|
||||
auto result = BN_div_word(raw(), word);
|
||||
if (result == (BN_ULONG)-1) {
|
||||
BigNum &setModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || b.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || b.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_add(raw(), a.raw(), b.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return result;
|
||||
return *this;
|
||||
}
|
||||
void setModSub(
|
||||
BigNum &setModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
@@ -148,9 +178,12 @@ public:
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setModMul(
|
||||
BigNum &setModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &m,
|
||||
@@ -163,9 +196,29 @@ public:
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
void setModExp(
|
||||
BigNum &setModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &m,
|
||||
const Context &context = Context()) {
|
||||
if (a.failed() || m.failed()) {
|
||||
_failed = true;
|
||||
} else if (a.isNegative() || m.isNegative()) {
|
||||
_failed = true;
|
||||
} else if (!BN_mod_inverse(raw(), a.raw(), m.raw(), context.raw())) {
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &setModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &m,
|
||||
@@ -178,23 +231,34 @@ public:
|
||||
_failed = true;
|
||||
} else if (isNegative()) {
|
||||
_failed = true;
|
||||
} else {
|
||||
_failed = false;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isNegative() const {
|
||||
return failed() ? false : BN_is_negative(raw());
|
||||
[[nodiscard]] bool isZero() const {
|
||||
return !failed() && (!_data || BN_is_zero(raw()));
|
||||
}
|
||||
|
||||
bool isPrime(const Context &context = Context()) const {
|
||||
if (failed()) {
|
||||
[[nodiscard]] bool isOne() const {
|
||||
return !failed() && _data && BN_is_one(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isNegative() const {
|
||||
return !failed() && _data && BN_is_negative(raw());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isPrime(const Context &context = Context()) const {
|
||||
if (failed() || !_data) {
|
||||
return false;
|
||||
}
|
||||
constexpr auto kMillerRabinIterationCount = 30;
|
||||
auto result = BN_is_prime_ex(
|
||||
const auto result = BN_is_prime_ex(
|
||||
raw(),
|
||||
kMillerRabinIterationCount,
|
||||
context.raw(),
|
||||
NULL);
|
||||
nullptr);
|
||||
if (result == 1) {
|
||||
return true;
|
||||
} else if (result != 0) {
|
||||
@@ -203,27 +267,42 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
BN_ULONG modWord(BN_ULONG word) const {
|
||||
Expects(word != 0);
|
||||
BigNum &subWord(unsigned int word) {
|
||||
if (failed()) {
|
||||
return (BN_ULONG)-1;
|
||||
return *this;
|
||||
} else if (!BN_sub_word(raw(), word)) {
|
||||
_failed = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
BigNum &divWord(BN_ULONG word, BN_ULONG *mod = nullptr) {
|
||||
Expects(word != 0);
|
||||
|
||||
auto result = BN_mod_word(raw(), word);
|
||||
const auto result = failed()
|
||||
? (BN_ULONG)-1
|
||||
: BN_div_word(raw(), word);
|
||||
if (result == (BN_ULONG)-1) {
|
||||
_failed = true;
|
||||
}
|
||||
return result;
|
||||
if (mod) {
|
||||
*mod = result;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] BN_ULONG countModWord(BN_ULONG word) const {
|
||||
Expects(word != 0);
|
||||
|
||||
return failed() ? (BN_ULONG)-1 : BN_mod_word(raw(), word);
|
||||
}
|
||||
|
||||
int bitsSize() const {
|
||||
[[nodiscard]] int bitsSize() const {
|
||||
return failed() ? 0 : BN_num_bits(raw());
|
||||
}
|
||||
int bytesSize() const {
|
||||
[[nodiscard]] int bytesSize() const {
|
||||
return failed() ? 0 : BN_num_bytes(raw());
|
||||
}
|
||||
|
||||
bytes::vector getBytes() const {
|
||||
[[nodiscard]] bytes::vector getBytes() const {
|
||||
if (failed()) {
|
||||
return {};
|
||||
}
|
||||
@@ -236,73 +315,84 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
BIGNUM *raw() {
|
||||
[[nodiscard]] BIGNUM *raw() {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
const BIGNUM *raw() const {
|
||||
[[nodiscard]] const BIGNUM *raw() const {
|
||||
if (!_data) _data = BN_new();
|
||||
return _data;
|
||||
}
|
||||
BIGNUM *takeRaw() {
|
||||
return base::take(_data);
|
||||
[[nodiscard]] BIGNUM *takeRaw() {
|
||||
return _failed
|
||||
? nullptr
|
||||
: _data
|
||||
? std::exchange(_data, nullptr)
|
||||
: BN_new();
|
||||
}
|
||||
|
||||
bool failed() const {
|
||||
[[nodiscard]] bool failed() const {
|
||||
return _failed;
|
||||
}
|
||||
|
||||
static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||
BigNum result;
|
||||
result.setAdd(a, b);
|
||||
return result;
|
||||
[[nodiscard]] static BigNum Add(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setAdd(a, b);
|
||||
}
|
||||
static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||
BigNum result;
|
||||
result.setSub(a, b);
|
||||
return result;
|
||||
[[nodiscard]] static BigNum Sub(const BigNum &a, const BigNum &b) {
|
||||
return BigNum().setSub(a, b);
|
||||
}
|
||||
static BigNum Mul(
|
||||
[[nodiscard]] static BigNum Mul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setMul(a, b, context);
|
||||
return result;
|
||||
return BigNum().setMul(a, b, context);
|
||||
}
|
||||
static BigNum ModSub(
|
||||
[[nodiscard]] static BigNum ModAdd(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setModSub(a, b, mod, context);
|
||||
return result;
|
||||
return BigNum().setModAdd(a, b, mod, context);
|
||||
}
|
||||
static BigNum ModMul(
|
||||
[[nodiscard]] static BigNum ModSub(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setModMul(a, b, mod, context);
|
||||
return result;
|
||||
return BigNum().setModSub(a, b, mod, context);
|
||||
}
|
||||
static BigNum ModExp(
|
||||
[[nodiscard]] static BigNum ModMul(
|
||||
const BigNum &a,
|
||||
const BigNum &b,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModMul(a, b, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModInverse(
|
||||
const BigNum &a,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
return BigNum().setModInverse(a, mod, context);
|
||||
}
|
||||
[[nodiscard]] static BigNum ModExp(
|
||||
const BigNum &base,
|
||||
const BigNum &power,
|
||||
const BigNum &mod,
|
||||
const Context &context = Context()) {
|
||||
BigNum result;
|
||||
result.setModExp(base, power, mod, context);
|
||||
return result;
|
||||
return BigNum().setModExp(base, power, mod, context);
|
||||
}
|
||||
static BigNum Failed() {
|
||||
BigNum result;
|
||||
[[nodiscard]] static BigNum Failed() {
|
||||
auto result = BigNum();
|
||||
result._failed = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
BIGNUM *_data = nullptr;
|
||||
void clear() {
|
||||
BN_clear_free(std::exchange(_data, nullptr));
|
||||
}
|
||||
|
||||
mutable BIGNUM *_data = nullptr;
|
||||
mutable bool _failed = false;
|
||||
|
||||
};
|
||||
@@ -445,6 +535,24 @@ inline bytes::vector Pbkdf2Sha512(
|
||||
EVP_sha512());
|
||||
}
|
||||
|
||||
inline bytes::vector HmacSha256(
|
||||
bytes::const_span key,
|
||||
bytes::const_span data) {
|
||||
auto result = bytes::vector(kSha256Size);
|
||||
auto length = (unsigned int)kSha256Size;
|
||||
|
||||
HMAC(
|
||||
EVP_sha256(),
|
||||
key.data(),
|
||||
key.size(),
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()),
|
||||
&length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace openssl
|
||||
|
||||
namespace bytes {
|
||||
|
||||
180
Telegram/SourceFiles/base/unixtime.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "base/unixtime.h"
|
||||
|
||||
#include "logs.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#elif defined Q_OS_MAC
|
||||
#include <mach/mach_time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace unixtime {
|
||||
namespace {
|
||||
|
||||
std::atomic<bool> ValueUpdated/* = false*/;
|
||||
std::atomic<TimeId> ValueShift/* = 0*/;
|
||||
std::atomic<bool> HttpValueValid/* = false*/;
|
||||
std::atomic<TimeId> HttpValueShift/* = 0*/;
|
||||
|
||||
class MsgIdManager {
|
||||
public:
|
||||
MsgIdManager();
|
||||
|
||||
void update();
|
||||
[[nodiscard]] uint64 next();
|
||||
|
||||
private:
|
||||
void initialize();
|
||||
|
||||
QReadWriteLock _lock;
|
||||
uint64 _startId = 0;
|
||||
std::atomic<uint32> _incrementedPart = 0;
|
||||
uint64 _startCounter = 0;
|
||||
uint64 _randomPart = 0;
|
||||
float64 _multiplier = 0.;
|
||||
|
||||
};
|
||||
|
||||
MsgIdManager GlobalMsgIdManager;
|
||||
|
||||
[[nodiscard]] float64 GetMultiplier() {
|
||||
// 0xFFFF0000 instead of 0x100000000 to make msgId grow slightly slower,
|
||||
// than unixtime and we had time to reconfigure.
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceFrequency(&li);
|
||||
return float64(0xFFFF0000L) / float64(li.QuadPart);
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
mach_timebase_info_data_t tb = { 0, 0 };
|
||||
mach_timebase_info(&tb);
|
||||
const auto frequency = (float64(tb.numer) / tb.denom) / 1000000.;
|
||||
return frequency * (float64(0xFFFF0000L) / 1000.);
|
||||
#else // Q_OS_MAC || Q_OS_WIN
|
||||
return float64(0xFFFF0000L) / 1000000000.;
|
||||
#endif // Q_OS_MAC || Q_OS_WIN
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64 GetCounter() {
|
||||
#ifdef Q_OS_WIN
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
return li.QuadPart;
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
return mach_absolute_time();
|
||||
#else // Q_OS_MAC || Q_OS_WIN
|
||||
timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return 1000000000 * uint64(ts.tv_sec) + uint64(ts.tv_nsec);
|
||||
#endif // Q_OS_MAC || Q_OS_WIN
|
||||
}
|
||||
|
||||
MsgIdManager::MsgIdManager() {
|
||||
auto generator = std::mt19937(std::random_device()());
|
||||
auto distribution = std::uniform_int_distribution<uint32>();
|
||||
_randomPart = distribution(generator);
|
||||
_multiplier = GetMultiplier();
|
||||
initialize();
|
||||
|
||||
srand(uint32(_startCounter & 0xFFFFFFFFUL));
|
||||
}
|
||||
|
||||
void MsgIdManager::update() {
|
||||
QWriteLocker lock(&_lock);
|
||||
initialize();
|
||||
}
|
||||
|
||||
void MsgIdManager::initialize() {
|
||||
_startCounter = GetCounter();
|
||||
_startId = ((uint64(uint32(now()))) << 32) | _randomPart;
|
||||
}
|
||||
|
||||
uint64 MsgIdManager::next() {
|
||||
const auto counter = GetCounter();
|
||||
|
||||
QReadLocker lock(&_lock);
|
||||
const auto delta = (counter - _startCounter);
|
||||
const auto result = _startId + (uint64)floor(delta * _multiplier);
|
||||
lock.unlock();
|
||||
|
||||
return (result & ~0x03L) + (_incrementedPart += 4);
|
||||
}
|
||||
|
||||
TimeId local() {
|
||||
return (TimeId)time(nullptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TimeId now() {
|
||||
return local() + ValueShift.load();
|
||||
}
|
||||
|
||||
void update(TimeId now, bool force) {
|
||||
if (force) {
|
||||
DEBUG_LOG(("MTP Info: forcing client unixtime to %1"
|
||||
).arg(now));
|
||||
ValueUpdated = true;
|
||||
} else {
|
||||
auto expected = false;
|
||||
if (!ValueUpdated.compare_exchange_strong(expected, true)) {
|
||||
return;
|
||||
}
|
||||
DEBUG_LOG(("MTP Info: setting client unixtime to %1").arg(now));
|
||||
}
|
||||
const auto shift = now + 1 - local();
|
||||
ValueShift = shift;
|
||||
DEBUG_LOG(("MTP Info: now unixtimeDelta is %1").arg(shift));
|
||||
|
||||
HttpValueShift = 0;
|
||||
HttpValueValid = false;
|
||||
|
||||
GlobalMsgIdManager.update();
|
||||
}
|
||||
|
||||
QDateTime parse(TimeId value) {
|
||||
return (value > 0)
|
||||
? QDateTime::fromTime_t(value - ValueShift)
|
||||
: QDateTime();
|
||||
}
|
||||
|
||||
TimeId serialize(const QDateTime &date) {
|
||||
return date.isNull() ? TimeId(0) : date.toTime_t() + ValueShift;
|
||||
}
|
||||
|
||||
bool http_valid() {
|
||||
return HttpValueValid;
|
||||
}
|
||||
|
||||
TimeId http_now() {
|
||||
return now() + HttpValueShift;
|
||||
}
|
||||
|
||||
void http_update(TimeId now) {
|
||||
HttpValueShift = now - base::unixtime::now();
|
||||
HttpValueValid = true;
|
||||
}
|
||||
|
||||
void http_invalidate() {
|
||||
HttpValueValid = false;
|
||||
}
|
||||
|
||||
uint64 mtproto_msg_id() {
|
||||
return GlobalMsgIdManager.next();
|
||||
}
|
||||
|
||||
} // namespace unixtime
|
||||
} // namespace base
|
||||
33
Telegram/SourceFiles/base/unixtime.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/basic_types.h"
|
||||
|
||||
class QDateTime;
|
||||
|
||||
namespace base {
|
||||
namespace unixtime {
|
||||
|
||||
// All functions are thread-safe.
|
||||
|
||||
[[nodiscard]] TimeId now();
|
||||
void update(TimeId now, bool force = false);
|
||||
|
||||
[[nodiscard]] QDateTime parse(TimeId value);
|
||||
[[nodiscard]] TimeId serialize(const QDateTime &date);
|
||||
|
||||
[[nodiscard]] bool http_valid();
|
||||
[[nodiscard]] TimeId http_now();
|
||||
void http_update(TimeId now);
|
||||
void http_invalidate();
|
||||
|
||||
[[nodiscard]] uint64 mtproto_msg_id();
|
||||
|
||||
} // namespace unixtime
|
||||
} // namespace base
|
||||
@@ -20,6 +20,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/update_checker.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QDesktopServices>
|
||||
|
||||
namespace {
|
||||
|
||||
rpl::producer<TextWithEntities> Text1() {
|
||||
@@ -100,7 +104,7 @@ void AboutBox::showVersionHistory() {
|
||||
}
|
||||
url = url.arg(qsl("talpha%1_%2").arg(cRealAlphaVersion()).arg(Core::countAlphaVersionSignature(cRealAlphaVersion())));
|
||||
|
||||
QApplication::clipboard()->setText(url);
|
||||
QGuiApplication::clipboard()->setText(url);
|
||||
|
||||
Ui::show(Box<InformBox>("The link to the current private alpha version of Telegram Desktop was copied to the clipboard."));
|
||||
} else {
|
||||
|
||||
@@ -11,15 +11,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_profile.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "base/timer.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
struct AbstractBox::LoadingProgress {
|
||||
LoadingProgress(
|
||||
Fn<void()> &&callback,
|
||||
const style::InfiniteRadialAnimation &st);
|
||||
|
||||
Ui::InfiniteRadialAnimation animation;
|
||||
base::Timer removeTimer;
|
||||
};
|
||||
|
||||
AbstractBox::LoadingProgress::LoadingProgress(
|
||||
Fn<void()> &&callback,
|
||||
const style::InfiniteRadialAnimation &st)
|
||||
: animation(std::move(callback), st) {
|
||||
}
|
||||
|
||||
void BoxContent::setTitle(rpl::producer<QString> title) {
|
||||
getDelegate()->setTitle(std::move(title) | Ui::Text::ToWithEntities());
|
||||
}
|
||||
@@ -269,6 +286,8 @@ AbstractBox::AbstractBox(
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
AbstractBox::~AbstractBox() = default;
|
||||
|
||||
void AbstractBox::setLayerType(bool layerType) {
|
||||
_layerType = layerType;
|
||||
updateTitlePosition();
|
||||
@@ -279,15 +298,35 @@ int AbstractBox::titleHeight() const {
|
||||
}
|
||||
|
||||
int AbstractBox::buttonsHeight() const {
|
||||
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
|
||||
const auto padding = _layerType
|
||||
? st::boxLayerButtonPadding
|
||||
: st::boxButtonPadding;
|
||||
return padding.top() + st::defaultBoxButton.height + padding.bottom();
|
||||
}
|
||||
|
||||
int AbstractBox::buttonsTop() const {
|
||||
auto padding = _layerType ? st::boxLayerButtonPadding : st::boxButtonPadding;
|
||||
const auto padding = _layerType
|
||||
? st::boxLayerButtonPadding
|
||||
: st::boxButtonPadding;
|
||||
return height() - padding.bottom() - st::defaultBoxButton.height;
|
||||
}
|
||||
|
||||
QRect AbstractBox::loadingRect() const {
|
||||
const auto padding = _layerType
|
||||
? st::boxLayerButtonPadding
|
||||
: st::boxButtonPadding;
|
||||
const auto size = st::boxLoadingSize;
|
||||
const auto skipx = _layerType
|
||||
? st::boxLayerTitlePosition.x()
|
||||
: st::boxTitlePosition.x();
|
||||
const auto skipy = (st::defaultBoxButton.height - size) / 2;
|
||||
return QRect(
|
||||
skipx,
|
||||
height() - padding.bottom() - skipy - size,
|
||||
size,
|
||||
size);
|
||||
}
|
||||
|
||||
void AbstractBox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto clip = e->rect();
|
||||
@@ -309,6 +348,14 @@ void AbstractBox::paintEvent(QPaintEvent *e) {
|
||||
&& clip.intersects(QRect(0, 0, width(), titleHeight()))) {
|
||||
paintAdditionalTitle(p);
|
||||
}
|
||||
if (_loadingProgress) {
|
||||
const auto rect = loadingRect();
|
||||
_loadingProgress->animation.draw(
|
||||
p,
|
||||
rect.topLeft(),
|
||||
rect.size(),
|
||||
width());
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractBox::paintAdditionalTitle(Painter &p) {
|
||||
@@ -441,6 +488,36 @@ QPointer<Ui::IconButton> AbstractBox::addTopButton(const style::IconButton &st,
|
||||
return result;
|
||||
}
|
||||
|
||||
void AbstractBox::showLoading(bool show) {
|
||||
const auto &st = st::boxLoadingAnimation;
|
||||
if (!show) {
|
||||
if (_loadingProgress && !_loadingProgress->removeTimer.isActive()) {
|
||||
_loadingProgress->removeTimer.callOnce(
|
||||
st.sineDuration + st.sinePeriod);
|
||||
_loadingProgress->animation.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!_loadingProgress) {
|
||||
const auto callback = [=] {
|
||||
if (!anim::Disabled()) {
|
||||
const auto t = st::boxLoadingAnimation.thickness;
|
||||
update(loadingRect().marginsAdded({ t, t, t, t }));
|
||||
}
|
||||
};
|
||||
_loadingProgress = std::make_unique<LoadingProgress>(
|
||||
callback,
|
||||
st::boxLoadingAnimation);
|
||||
_loadingProgress->removeTimer.setCallback([=] {
|
||||
_loadingProgress = nullptr;
|
||||
});
|
||||
} else {
|
||||
_loadingProgress->removeTimer.cancel();
|
||||
}
|
||||
_loadingProgress->animation.start();
|
||||
}
|
||||
|
||||
|
||||
void AbstractBox::setDimensions(int newWidth, int maxHeight, bool forceCenterPosition) {
|
||||
_maxContentHeight = maxHeight;
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
virtual QPointer<Ui::IconButton> addTopButton(
|
||||
const style::IconButton &st,
|
||||
Fn<void()> clickCallback) = 0;
|
||||
virtual void showLoading(bool show) = 0;
|
||||
virtual void updateButtonsPositions() = 0;
|
||||
|
||||
virtual void showBox(
|
||||
@@ -133,6 +134,9 @@ public:
|
||||
std::move(clickCallback),
|
||||
st);
|
||||
}
|
||||
void showLoading(bool show) {
|
||||
getDelegate()->showLoading(show);
|
||||
}
|
||||
void updateButtonsGeometry() {
|
||||
getDelegate()->updateButtonsPositions();
|
||||
}
|
||||
@@ -271,6 +275,7 @@ public:
|
||||
AbstractBox(
|
||||
not_null<Window::LayerStackWidget*> layer,
|
||||
object_ptr<BoxContent> content);
|
||||
~AbstractBox();
|
||||
|
||||
void parentResized() override;
|
||||
|
||||
@@ -294,6 +299,7 @@ public:
|
||||
QPointer<Ui::IconButton> addTopButton(
|
||||
const style::IconButton &st,
|
||||
Fn<void()> clickCallback) override;
|
||||
void showLoading(bool show) override;
|
||||
void updateButtonsPositions() override;
|
||||
QPointer<QWidget> outerContainer() override;
|
||||
|
||||
@@ -332,17 +338,20 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
struct LoadingProgress;
|
||||
|
||||
void paintAdditionalTitle(Painter &p);
|
||||
void updateTitlePosition();
|
||||
void refreshLang();
|
||||
|
||||
bool hasTitle() const;
|
||||
int titleHeight() const;
|
||||
int buttonsHeight() const;
|
||||
int buttonsTop() const;
|
||||
int contentTop() const;
|
||||
int countFullHeight() const;
|
||||
int countRealHeight() const;
|
||||
[[nodiscard]] bool hasTitle() const;
|
||||
[[nodiscard]] int titleHeight() const;
|
||||
[[nodiscard]] int buttonsHeight() const;
|
||||
[[nodiscard]] int buttonsTop() const;
|
||||
[[nodiscard]] int contentTop() const;
|
||||
[[nodiscard]] int countFullHeight() const;
|
||||
[[nodiscard]] int countRealHeight() const;
|
||||
[[nodiscard]] QRect loadingRect() const;
|
||||
void updateSize();
|
||||
|
||||
not_null<Window::LayerStackWidget*> _layer;
|
||||
@@ -363,6 +372,7 @@ private:
|
||||
std::vector<object_ptr<Ui::RoundButton>> _buttons;
|
||||
object_ptr<Ui::RoundButton> _leftButton = { nullptr };
|
||||
base::unique_qptr<Ui::IconButton> _topButton = { nullptr };
|
||||
std::unique_ptr<LoadingProgress> _loadingProgress;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/confirm_phone_box.h" // ExtractPhonePrefix.
|
||||
#include "boxes/photo_crop_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
@@ -21,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/application.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -37,11 +39,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwindow.h"
|
||||
#include "apiwrap.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxGroupChannelTitle = 128; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxUserFirstLastName = 64; // See also edit_contact_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
|
||||
@@ -56,6 +62,48 @@ bool IsValidPhone(QString phone) {
|
||||
|| phone == qsl("4242")));
|
||||
}
|
||||
|
||||
void ChatCreateDone(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
QImage image,
|
||||
const MTPUpdates &updates) {
|
||||
navigation->session().api().applyUpdates(updates);
|
||||
|
||||
auto success = base::make_optional(&updates)
|
||||
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats().v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats().v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 "
|
||||
"(GroupInfoBox::creationDone)").arg(updates->type()));
|
||||
return std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return (!chats->empty()
|
||||
&& chats->front().type() == mtpc_chat)
|
||||
? base::make_optional(chats)
|
||||
: std::nullopt;
|
||||
}
|
||||
| [&](auto chats) {
|
||||
return navigation->session().data().chat(
|
||||
chats->front().c_chat().vid().v);
|
||||
}
|
||||
| [&](not_null<ChatData*> chat) {
|
||||
if (!image.isNull()) {
|
||||
chat->session().api().uploadPeerPhoto(
|
||||
chat,
|
||||
std::move(image));
|
||||
}
|
||||
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: chat not found in updates "
|
||||
"(ContactsBox::creationDone)"));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
style::InputField CreateBioFieldStyle() {
|
||||
@@ -83,7 +131,7 @@ void ShowAddParticipantsError(
|
||||
if (error == qstr("USER_BOT")) {
|
||||
const auto channel = chat->asChannel();
|
||||
if ((users.size() == 1)
|
||||
&& (users.front()->botInfo != nullptr)
|
||||
&& users.front()->isBot()
|
||||
&& channel
|
||||
&& !channel->isMegagroup()
|
||||
&& channel->canAddAdmins()) {
|
||||
@@ -103,7 +151,8 @@ void ShowAddParticipantsError(
|
||||
auto box = Box<EditAdminBox>(
|
||||
channel,
|
||||
user,
|
||||
MTP_chatAdminRights(MTP_flags(0)));
|
||||
MTP_chatAdminRights(MTP_flags(0)),
|
||||
QString());
|
||||
box->setSaveCallback(saveCallback);
|
||||
*weak = Ui::show(std::move(box));
|
||||
};
|
||||
@@ -117,9 +166,7 @@ void ShowAddParticipantsError(
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto bot = ranges::find_if(users, [](not_null<UserData*> user) {
|
||||
return user->botInfo != nullptr;
|
||||
});
|
||||
const auto bot = ranges::find_if(users, &UserData::isBot);
|
||||
const auto hasBot = (bot != end(users));
|
||||
const auto text = [&] {
|
||||
if (error == qstr("USER_BOT")) {
|
||||
@@ -155,7 +202,10 @@ void ShowAddParticipantsError(
|
||||
|
||||
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
|
||||
public:
|
||||
Inner(QWidget *parent, Fn<void()> revokeCallback);
|
||||
Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session,
|
||||
Fn<void()> revokeCallback);
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
@@ -174,6 +224,8 @@ private:
|
||||
void paintChat(Painter &p, const ChatRow &row, bool selected) const;
|
||||
void updateSelected();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
PeerData *_selected = nullptr;
|
||||
PeerData *_pressed = nullptr;
|
||||
|
||||
@@ -189,38 +241,42 @@ private:
|
||||
|
||||
};
|
||||
|
||||
AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString phone)
|
||||
: _first(this, st::defaultInputField, tr::lng_signup_firstname(), fname)
|
||||
AddContactBox::AddContactBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session)
|
||||
: AddContactBox(nullptr, session, QString(), QString(), QString()) {
|
||||
}
|
||||
|
||||
AddContactBox::AddContactBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
QString fname,
|
||||
QString lname,
|
||||
QString phone)
|
||||
: _session(session)
|
||||
, _first(this, st::defaultInputField, tr::lng_signup_firstname(), fname)
|
||||
, _last(this, st::defaultInputField, tr::lng_signup_lastname(), lname)
|
||||
, _phone(this, st::defaultInputField, tr::lng_contact_phone(), phone)
|
||||
, _phone(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_contact_phone(),
|
||||
ExtractPhonePrefix(session->user()->phone()),
|
||||
phone)
|
||||
, _invertOrder(langFirstNameGoesSecond()) {
|
||||
if (!phone.isEmpty()) {
|
||||
_phone->setDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
AddContactBox::AddContactBox(QWidget*, UserData *user)
|
||||
: _user(user)
|
||||
, _first(this, st::defaultInputField, tr::lng_signup_firstname(), user->firstName)
|
||||
, _last(this, st::defaultInputField, tr::lng_signup_lastname(), user->lastName)
|
||||
, _phone(this, st::defaultInputField, tr::lng_contact_phone(), user->phone())
|
||||
, _invertOrder(langFirstNameGoesSecond()) {
|
||||
_phone->setDisabled(true);
|
||||
}
|
||||
|
||||
void AddContactBox::prepare() {
|
||||
if (_invertOrder) {
|
||||
setTabOrder(_last, _first);
|
||||
}
|
||||
if (_user) {
|
||||
setTitle(tr::lng_edit_contact_title());
|
||||
} else {
|
||||
const auto readyToAdd = !_phone->getLastText().isEmpty()
|
||||
&& (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty());
|
||||
setTitle(readyToAdd
|
||||
? tr::lng_confirm_contact_data()
|
||||
: tr::lng_enter_contact_data());
|
||||
}
|
||||
const auto readyToAdd = !_phone->getLastText().isEmpty()
|
||||
&& (!_first->getLastText().isEmpty() || !_last->getLastText().isEmpty());
|
||||
setTitle(readyToAdd
|
||||
? tr::lng_confirm_contact_data()
|
||||
: tr::lng_enter_contact_data());
|
||||
updateButtons();
|
||||
|
||||
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
|
||||
@@ -294,7 +350,9 @@ void AddContactBox::submit() {
|
||||
}
|
||||
|
||||
void AddContactBox::save() {
|
||||
if (_addRequest) return;
|
||||
if (_addRequest) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto firstName = TextUtilities::PrepareForSending(_first->getLastText());
|
||||
auto lastName = TextUtilities::PrepareForSending(_last->getLastText());
|
||||
@@ -308,7 +366,7 @@ void AddContactBox::save() {
|
||||
_first->showError();
|
||||
}
|
||||
return;
|
||||
} else if (!_user && !IsValidPhone(phone)) {
|
||||
} else if (!IsValidPhone(phone)) {
|
||||
_phone->setFocus();
|
||||
_phone->showError();
|
||||
return;
|
||||
@@ -318,70 +376,44 @@ void AddContactBox::save() {
|
||||
lastName = QString();
|
||||
}
|
||||
_sentName = firstName;
|
||||
if (_user) {
|
||||
_contactId = rand_value<uint64>();
|
||||
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone()), MTP_string(firstName), MTP_string(lastName)));
|
||||
_addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector<MTPInputContact>(v)), rpcDone(&AddContactBox::onSaveUserDone), rpcFail(&AddContactBox::onSaveUserFail));
|
||||
} else {
|
||||
_contactId = rand_value<uint64>();
|
||||
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(phone), MTP_string(firstName), MTP_string(lastName)));
|
||||
_addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector<MTPInputContact>(v)), rpcDone(&AddContactBox::onImportDone));
|
||||
}
|
||||
}
|
||||
_contactId = rand_value<uint64>();
|
||||
_addRequest = _session->api().request(MTPcontacts_ImportContacts(
|
||||
MTP_vector<MTPInputContact>(
|
||||
1,
|
||||
MTP_inputPhoneContact(
|
||||
MTP_long(_contactId),
|
||||
MTP_string(phone),
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName)))
|
||||
)).done(crl::guard(this, [=](
|
||||
const MTPcontacts_ImportedContacts &result) {
|
||||
result.match([&](const MTPDcontacts_importedContacts &data) {
|
||||
_session->data().processUsers(data.vusers());
|
||||
|
||||
bool AddContactBox::onSaveUserFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_addRequest = 0;
|
||||
const auto &err = error.type();
|
||||
const auto firstName = _first->getLastText().trimmed();
|
||||
const auto lastName = _last->getLastText().trimmed();
|
||||
if (err == "CHAT_TITLE_NOT_MODIFIED") {
|
||||
_user->setName(firstName, lastName, _user->nameOrPhone, _user->username);
|
||||
closeBox();
|
||||
return true;
|
||||
} else if (err == "NO_CHAT_TITLE") {
|
||||
_first->setFocus();
|
||||
_first->showError();
|
||||
return true;
|
||||
}
|
||||
_first->setFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
|
||||
if (!isBoxShown() || !App::main()) return;
|
||||
|
||||
const auto &d = res.c_contacts_importedContacts();
|
||||
Auth().data().processUsers(d.vusers());
|
||||
|
||||
const auto &v = d.vimported().v;
|
||||
const auto user = [&]() -> UserData* {
|
||||
if (!v.isEmpty()) {
|
||||
auto &c = v.front().c_importedContact();
|
||||
if (c.vclient_id().v == _contactId) {
|
||||
return Auth().data().userLoaded(c.vuser_id().v);
|
||||
const auto extractUser = [&](const MTPImportedContact &data) {
|
||||
return data.match([&](const MTPDimportedContact &data) {
|
||||
return (data.vclient_id().v == _contactId)
|
||||
? _session->data().userLoaded(data.vuser_id().v)
|
||||
: nullptr;
|
||||
});
|
||||
};
|
||||
const auto &list = data.vimported().v;
|
||||
const auto user = list.isEmpty()
|
||||
? nullptr
|
||||
: extractUser(list.front());
|
||||
if (user) {
|
||||
if (user->isContact() || user->session().supportMode()) {
|
||||
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
|
||||
}
|
||||
Ui::hideLayer();
|
||||
} else if (isBoxShown()) {
|
||||
hideChildren();
|
||||
_retrying = true;
|
||||
updateButtons();
|
||||
update();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (user) {
|
||||
if (user->isContact() || user->session().supportMode()) {
|
||||
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
|
||||
}
|
||||
Ui::hideLayer();
|
||||
} else {
|
||||
hideChildren();
|
||||
_retrying = true;
|
||||
updateButtons();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
|
||||
auto &d = res.c_contacts_importedContacts();
|
||||
Auth().data().processUsers(d.vusers());
|
||||
closeBox();
|
||||
});
|
||||
})).send();
|
||||
}
|
||||
|
||||
void AddContactBox::retry() {
|
||||
@@ -403,19 +435,19 @@ void AddContactBox::updateButtons() {
|
||||
if (_retrying) {
|
||||
addButton(tr::lng_try_other_contact(), [=] { retry(); });
|
||||
} else {
|
||||
addButton(
|
||||
_user ? tr::lng_settings_save() : tr::lng_add_contact(),
|
||||
[=] { save(); });
|
||||
addButton(tr::lng_add_contact(), [=] { save(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
}
|
||||
}
|
||||
|
||||
GroupInfoBox::GroupInfoBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
Type type,
|
||||
const QString &title,
|
||||
Fn<void(not_null<ChannelData*>)> channelDone)
|
||||
: _type(type)
|
||||
: _navigation(navigation)
|
||||
, _type(type)
|
||||
, _initialTitle(title)
|
||||
, _channelDone(std::move(channelDone)) {
|
||||
}
|
||||
@@ -439,10 +471,12 @@ void GroupInfoBox::prepare() {
|
||||
_initialTitle);
|
||||
_title->setMaxLength(kMaxGroupChannelTitle);
|
||||
_title->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_title ->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
_title->setInstantReplacesEnabled(
|
||||
_navigation->session().settings().replaceEmojiValue());
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
getDelegate()->outerContainer(),
|
||||
_title);
|
||||
_title,
|
||||
&_navigation->session());
|
||||
|
||||
if (_type != Type::Group) {
|
||||
_description.create(
|
||||
@@ -454,7 +488,7 @@ void GroupInfoBox::prepare() {
|
||||
_description->setMaxLength(kMaxChannelDescription);
|
||||
_description->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_description->setInstantReplacesEnabled(
|
||||
Global::ReplaceEmojiValue());
|
||||
_navigation->session().settings().replaceEmojiValue());
|
||||
|
||||
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
|
||||
connect(_description, &Ui::InputField::submitted, [=] { submit(); });
|
||||
@@ -462,7 +496,8 @@ void GroupInfoBox::prepare() {
|
||||
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
getDelegate()->outerContainer(),
|
||||
_description);
|
||||
_description,
|
||||
&_navigation->session());
|
||||
}
|
||||
|
||||
connect(_title, &Ui::InputField::submitted, [=] { submitName(); });
|
||||
@@ -536,43 +571,10 @@ void GroupInfoBox::createGroup(
|
||||
MTP_string(title)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
auto image = _photo->takeResultImage();
|
||||
Ui::hideLayer();
|
||||
const auto navigation = _navigation;
|
||||
|
||||
Auth().api().applyUpdates(result);
|
||||
|
||||
auto success = base::make_optional(&result)
|
||||
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats().v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats().v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 "
|
||||
"(GroupInfoBox::creationDone)").arg(updates->type()));
|
||||
return std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return (!chats->empty()
|
||||
&& chats->front().type() == mtpc_chat)
|
||||
? base::make_optional(chats)
|
||||
: std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return Auth().data().chat(chats->front().c_chat().vid().v);
|
||||
}
|
||||
| [&](not_null<ChatData*> chat) {
|
||||
if (!image.isNull()) {
|
||||
chat->session().api().uploadPeerPhoto(
|
||||
chat,
|
||||
std::move(image));
|
||||
}
|
||||
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
|
||||
};
|
||||
if (!success) {
|
||||
LOG(("API Error: chat not found in updates "
|
||||
"(ContactsBox::creationDone)"));
|
||||
}
|
||||
Ui::hideLayer(); // Destroys 'this'.
|
||||
ChatCreateDone(navigation, std::move(image), result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_creationRequestId = 0;
|
||||
if (error.type() == qstr("NO_CHAT_TITLE")) {
|
||||
@@ -630,7 +632,7 @@ void GroupInfoBox::submit() {
|
||||
};
|
||||
Ui::show(
|
||||
Box<PeerListBox>(
|
||||
std::make_unique<AddParticipantsBoxController>(),
|
||||
std::make_unique<AddParticipantsBoxController>(_navigation),
|
||||
std::move(initBox)),
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
@@ -647,7 +649,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
||||
MTPInputGeoPoint(), // geo_point
|
||||
MTPstring() // address
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
Auth().api().applyUpdates(result);
|
||||
_navigation->session().api().applyUpdates(result);
|
||||
|
||||
const auto success = base::make_optional(&result)
|
||||
| [](auto updates) -> std::optional<const QVector<MTPChat>*> {
|
||||
@@ -665,8 +667,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
||||
? base::make_optional(chats)
|
||||
: std::nullopt;
|
||||
}
|
||||
| [](auto chats) {
|
||||
return Auth().data().channel(chats->front().c_channel().vid().v);
|
||||
| [&](auto chats) {
|
||||
return _navigation->session().data().channel(
|
||||
chats->front().c_channel().vid().v);
|
||||
}
|
||||
| [&](not_null<ChannelData*> channel) {
|
||||
auto image = _photo->takeResultImage();
|
||||
@@ -690,7 +693,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
||||
closeBox();
|
||||
callback(argument);
|
||||
} else {
|
||||
Ui::show(Box<SetupChannelBox>(_createdChannel));
|
||||
Ui::show(Box<SetupChannelBox>(
|
||||
_navigation,
|
||||
_createdChannel));
|
||||
}
|
||||
}).send();
|
||||
};
|
||||
@@ -730,10 +735,16 @@ void GroupInfoBox::updateMaxHeight() {
|
||||
setDimensions(st::boxWideWidth, newHeight);
|
||||
}
|
||||
|
||||
SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
|
||||
: _channel(channel)
|
||||
SetupChannelBox::SetupChannelBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
bool existing)
|
||||
: _navigation(navigation)
|
||||
, _channel(channel)
|
||||
, _existing(existing)
|
||||
, _privacyGroup(std::make_shared<Ui::RadioenumGroup<Privacy>>(Privacy::Public))
|
||||
, _privacyGroup(
|
||||
std::make_shared<Ui::RadioenumGroup<Privacy>>(Privacy::Public))
|
||||
, _public(
|
||||
this,
|
||||
_privacyGroup,
|
||||
@@ -750,7 +761,12 @@ SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
|
||||
? tr::lng_create_private_group_title
|
||||
: tr::lng_create_private_channel_title)(tr::now),
|
||||
st::defaultBoxCheckbox)
|
||||
, _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadio.diameter - st::defaultBoxCheckbox.textPosition.x())
|
||||
, _aboutPublicWidth(st::boxWideWidth
|
||||
- st::boxPadding.left()
|
||||
- st::boxButtonPadding.right()
|
||||
- st::newGroupPadding.left()
|
||||
- st::defaultRadio.diameter
|
||||
- st::defaultBoxCheckbox.textPosition.x())
|
||||
, _aboutPublic(
|
||||
st::defaultTextStyle,
|
||||
(channel->isMegagroup()
|
||||
@@ -800,7 +816,7 @@ void SetupChannelBox::prepare() {
|
||||
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (!_existing) {
|
||||
AddParticipantsBoxController::Start(_channel);
|
||||
AddParticipantsBoxController::Start(_navigation, _channel);
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
@@ -1004,11 +1020,16 @@ void SetupChannelBox::privacyChanged(Privacy value) {
|
||||
if (value == Privacy::Public) {
|
||||
if (_tooMuchUsernames) {
|
||||
_privacyGroup->setValue(Privacy::Private);
|
||||
Ui::show(Box<RevokePublicLinkBox>(crl::guard(this, [this] {
|
||||
const auto callback = crl::guard(this, [=] {
|
||||
_tooMuchUsernames = false;
|
||||
_privacyGroup->setValue(Privacy::Public);
|
||||
check();
|
||||
})), LayerOption::KeepOther);
|
||||
});
|
||||
Ui::show(
|
||||
Box<RevokePublicLinkBox>(
|
||||
&_channel->session(),
|
||||
callback),
|
||||
LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
_link->show();
|
||||
@@ -1097,23 +1118,31 @@ bool SetupChannelBox::onCheckFail(const RPCError &error) {
|
||||
}
|
||||
|
||||
void SetupChannelBox::showRevokePublicLinkBoxForEdit() {
|
||||
closeBox();
|
||||
Ui::show(Box<RevokePublicLinkBox>([channel = _channel, existing = _existing]() {
|
||||
const auto channel = _channel;
|
||||
const auto existing = _existing;
|
||||
const auto navigation = _navigation;
|
||||
const auto callback = [=] {
|
||||
Ui::show(
|
||||
Box<SetupChannelBox>(channel, existing),
|
||||
Box<SetupChannelBox>(navigation, channel, existing),
|
||||
LayerOption::KeepOther);
|
||||
}), LayerOption::KeepOther);
|
||||
};
|
||||
closeBox();
|
||||
Ui::show(
|
||||
Box<RevokePublicLinkBox>(
|
||||
&channel->session(),
|
||||
callback),
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
bool SetupChannelBox::onFirstCheckFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_checkRequestId = 0;
|
||||
QString err(error.type());
|
||||
if (err == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
|
||||
const auto &type = error.type();
|
||||
if (type == qstr("CHANNEL_PUBLIC_GROUP_NA")) {
|
||||
Ui::hideLayer();
|
||||
return true;
|
||||
} else if (err == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
|
||||
} else if (type == qstr("CHANNELS_ADMIN_PUBLIC_TOO_MUCH")) {
|
||||
if (_existing) {
|
||||
showRevokePublicLinkBoxForEdit();
|
||||
} else {
|
||||
@@ -1148,8 +1177,8 @@ void EditNameBox::prepare() {
|
||||
if (_invertOrder) {
|
||||
setTabOrder(_last, _first);
|
||||
}
|
||||
_first->setMaxLength(kMaxGroupChannelTitle);
|
||||
_last->setMaxLength(kMaxGroupChannelTitle);
|
||||
_first->setMaxLength(kMaxUserFirstLastName);
|
||||
_last->setMaxLength(kMaxUserFirstLastName);
|
||||
|
||||
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
|
||||
connect(_last, &Ui::InputField::submitted, [=] { submit(); });
|
||||
@@ -1249,7 +1278,12 @@ bool EditNameBox::saveSelfFail(const RPCError &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) : TWidget(parent)
|
||||
RevokePublicLinkBox::Inner::Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session,
|
||||
Fn<void()> revokeCallback)
|
||||
: TWidget(parent)
|
||||
, _session(session)
|
||||
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
||||
, _revokeWidth(st::normalFont->width(tr::lng_channels_too_much_public_revoke(tr::now)))
|
||||
, _revokeCallback(std::move(revokeCallback)) {
|
||||
@@ -1264,7 +1298,7 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
|
||||
return data.vchats().v;
|
||||
});
|
||||
for (const auto &chat : chats) {
|
||||
if (const auto peer = Auth().data().processChat(chat)) {
|
||||
if (const auto peer = _session->data().processChat(chat)) {
|
||||
if (!peer->isChannel() || peer->userName().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1290,8 +1324,10 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) :
|
||||
|
||||
RevokePublicLinkBox::RevokePublicLinkBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Fn<void()> revokeCallback)
|
||||
: _aboutRevoke(
|
||||
: _session(session)
|
||||
, _aboutRevoke(
|
||||
this,
|
||||
tr::lng_channels_too_much_public_about(tr::now),
|
||||
st::aboutRevokePublicLabel)
|
||||
@@ -1300,7 +1336,7 @@ RevokePublicLinkBox::RevokePublicLinkBox(
|
||||
|
||||
void RevokePublicLinkBox::prepare() {
|
||||
_innerTop = st::boxPadding.top() + _aboutRevoke->height() + st::boxPadding.top();
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this, [=] {
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this, _session, [=] {
|
||||
const auto callback = _revokeCallback;
|
||||
closeBox();
|
||||
if (callback) {
|
||||
@@ -1310,7 +1346,7 @@ void RevokePublicLinkBox::prepare() {
|
||||
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
|
||||
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
|
||||
|
||||
_inner->resizeToWidth(st::boxWideWidth);
|
||||
setDimensions(st::boxWideWidth, _innerTop + _inner->height());
|
||||
@@ -1324,7 +1360,7 @@ void RevokePublicLinkBox::Inner::updateSelected() {
|
||||
auto point = mapFromGlobal(QCursor::pos());
|
||||
PeerData *selected = nullptr;
|
||||
auto top = _rowsTop;
|
||||
for_const (auto &row, _rows) {
|
||||
for (const auto &row : _rows) {
|
||||
auto revokeLink = rtlrect(width() - st::contactsPadding.right() - st::contactsCheckPosition.x() - _revokeWidth, top + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _revokeWidth, st::normalFont->height, width());
|
||||
if (revokeLink.contains(point)) {
|
||||
selected = row.peer;
|
||||
|
||||
@@ -14,9 +14,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
class ConfirmBox;
|
||||
class PeerListBox;
|
||||
|
||||
constexpr auto kMaxBioLength = 70;
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
style::InputField CreateBioFieldStyle();
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class FlatLabel;
|
||||
@@ -32,21 +36,31 @@ class LinkButton;
|
||||
class UserpicButton;
|
||||
} // namespace Ui
|
||||
|
||||
constexpr auto kMaxBioLength = 70;
|
||||
|
||||
enum class PeerFloodType {
|
||||
Send,
|
||||
InviteGroup,
|
||||
InviteChannel,
|
||||
};
|
||||
|
||||
style::InputField CreateBioFieldStyle();
|
||||
|
||||
QString PeerFloodErrorText(PeerFloodType type);
|
||||
void ShowAddParticipantsError(
|
||||
const QString &error,
|
||||
not_null<PeerData*> chat,
|
||||
const std::vector<not_null<UserData*>> &users);
|
||||
|
||||
class AddContactBox : public BoxContent, public RPCSender {
|
||||
class AddContactBox : public BoxContent {
|
||||
public:
|
||||
AddContactBox(QWidget*, QString fname = QString(), QString lname = QString(), QString phone = QString());
|
||||
AddContactBox(QWidget*, UserData *user);
|
||||
AddContactBox(QWidget*, not_null<Main::Session*> session);
|
||||
AddContactBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
QString fname,
|
||||
QString lname,
|
||||
QString phone);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -61,12 +75,8 @@ private:
|
||||
void retry();
|
||||
void save();
|
||||
void updateButtons();
|
||||
void onImportDone(const MTPcontacts_ImportedContacts &res);
|
||||
|
||||
void onSaveUserDone(const MTPcontacts_ImportedContacts &res);
|
||||
bool onSaveUserFail(const RPCError &e);
|
||||
|
||||
UserData *_user = nullptr;
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
object_ptr<Ui::InputField> _first;
|
||||
object_ptr<Ui::InputField> _last;
|
||||
@@ -91,6 +101,7 @@ public:
|
||||
};
|
||||
GroupInfoBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
Type type,
|
||||
const QString &title = QString(),
|
||||
Fn<void(not_null<ChannelData*>)> channelDone = nullptr);
|
||||
@@ -110,6 +121,8 @@ private:
|
||||
void descriptionResized();
|
||||
void updateMaxHeight();
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
|
||||
Type _type = Type::Group;
|
||||
QString _initialTitle;
|
||||
Fn<void(not_null<ChannelData*>)> _channelDone;
|
||||
@@ -126,7 +139,11 @@ private:
|
||||
|
||||
class SetupChannelBox : public BoxContent, public RPCSender {
|
||||
public:
|
||||
SetupChannelBox(QWidget*, ChannelData *channel, bool existing = false);
|
||||
SetupChannelBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
bool existing = false);
|
||||
|
||||
void setInnerFocus() override;
|
||||
|
||||
@@ -162,7 +179,9 @@ private:
|
||||
|
||||
void showRevokePublicLinkBoxForEdit();
|
||||
|
||||
ChannelData *_channel = nullptr;
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<ChannelData*> _channel;
|
||||
|
||||
bool _existing = false;
|
||||
|
||||
std::shared_ptr<Ui::RadioenumGroup<Privacy>> _privacyGroup;
|
||||
@@ -215,7 +234,10 @@ private:
|
||||
|
||||
class RevokePublicLinkBox : public BoxContent, public RPCSender {
|
||||
public:
|
||||
RevokePublicLinkBox(QWidget*, Fn<void()> revokeCallback);
|
||||
RevokePublicLinkBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Fn<void()> revokeCallback);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -223,6 +245,8 @@ protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _aboutRevoke;
|
||||
|
||||
class Inner;
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/auto_download_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "info/profile/info_profile_button.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
@@ -29,8 +29,10 @@ constexpr auto kDefaultLimit = 10 * kMegabyte;
|
||||
|
||||
AutoDownloadBox::AutoDownloadBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Data::AutoDownload::Source source)
|
||||
: _source(source) {
|
||||
: _session(session)
|
||||
, _source(source) {
|
||||
}
|
||||
|
||||
void AutoDownloadBox::prepare() {
|
||||
@@ -45,7 +47,7 @@ void AutoDownloadBox::setupContent() {
|
||||
|
||||
setTitle(tr::lng_media_auto_title());
|
||||
|
||||
const auto settings = &Auth().settings().autoDownload();
|
||||
const auto settings = &_session->settings().autoDownload();
|
||||
const auto checked = [=](Source source, Type type) {
|
||||
return (settings->bytesLimit(source, type) > 0);
|
||||
};
|
||||
@@ -166,11 +168,11 @@ void AutoDownloadBox::setupContent() {
|
||||
Local::writeUserSettings();
|
||||
}
|
||||
if (allowMoreTypes.contains(Type::Photo)) {
|
||||
Auth().data().photoLoadSettingsChanged();
|
||||
_session->data().photoLoadSettingsChanged();
|
||||
}
|
||||
if (ranges::find_if(allowMoreTypes, _1 != Type::Photo)
|
||||
!= allowMoreTypes.end()) {
|
||||
Auth().data().documentLoadSettingsChanged();
|
||||
_session->data().documentLoadSettingsChanged();
|
||||
}
|
||||
closeBox();
|
||||
});
|
||||
|
||||
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
namespace AutoDownload {
|
||||
enum class Source;
|
||||
@@ -17,7 +21,10 @@ enum class Source;
|
||||
|
||||
class AutoDownloadBox : public BoxContent {
|
||||
public:
|
||||
AutoDownloadBox(QWidget*, Data::AutoDownload::Source source);
|
||||
AutoDownloadBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Data::AutoDownload::Source source);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -25,6 +32,8 @@ protected:
|
||||
private:
|
||||
void setupContent();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
Data::AutoDownload::Source _source;
|
||||
|
||||
};
|
||||
|
||||
@@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
AutoLockBox::AutoLockBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
void AutoLockBox::prepare() {
|
||||
setTitle(tr::lng_passcode_autolock());
|
||||
|
||||
@@ -39,6 +43,6 @@ void AutoLockBox::durationChanged(int seconds) {
|
||||
Local::writeUserSettings();
|
||||
Global::RefLocalPasscodeChanged().notify();
|
||||
|
||||
Auth().checkAutoLock();
|
||||
_session->checkAutoLock();
|
||||
closeBox();
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class Radiobutton;
|
||||
} // namespace Ui
|
||||
|
||||
class AutoLockBox : public BoxContent {
|
||||
public:
|
||||
AutoLockBox(QWidget*) {
|
||||
}
|
||||
AutoLockBox(QWidget*, not_null<Main::Session*> session);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -24,6 +27,8 @@ protected:
|
||||
private:
|
||||
void durationChanged(int seconds);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
std::vector<object_ptr<Ui::Radiobutton>> _options;
|
||||
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/effects/round_checkbox.h"
|
||||
#include "ui/image/image.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -54,7 +54,9 @@ class BackgroundBox::Inner
|
||||
, private MTP::Sender
|
||||
, private base::Subscriber {
|
||||
public:
|
||||
Inner(QWidget *parent);
|
||||
Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session);
|
||||
|
||||
rpl::producer<Data::WallPaper> chooseEvents() const;
|
||||
rpl::producer<Data::WallPaper> removeRequests() const;
|
||||
@@ -107,6 +109,8 @@ private:
|
||||
int row) const;
|
||||
void validatePaperThumbnail(const Paper &paper) const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
std::vector<Paper> _papers;
|
||||
|
||||
Selection _over;
|
||||
@@ -118,7 +122,8 @@ private:
|
||||
|
||||
};
|
||||
|
||||
BackgroundBox::BackgroundBox(QWidget*) {
|
||||
BackgroundBox::BackgroundBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
void BackgroundBox::prepare() {
|
||||
@@ -128,11 +133,15 @@ void BackgroundBox::prepare() {
|
||||
|
||||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this), st::backgroundScroll);
|
||||
_inner = setInnerWidget(
|
||||
object_ptr<Inner>(this, _session),
|
||||
st::backgroundScroll);
|
||||
|
||||
_inner->chooseEvents(
|
||||
) | rpl::start_with_next([](const Data::WallPaper &paper) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(paper), LayerOption::KeepOther);
|
||||
) | rpl::start_with_next([=](const Data::WallPaper &paper) {
|
||||
Ui::show(
|
||||
Box<BackgroundPreviewBox>(_session, paper),
|
||||
LayerOption::KeepOther);
|
||||
}, _inner->lifetime());
|
||||
|
||||
_inner->removeRequests(
|
||||
@@ -143,6 +152,7 @@ void BackgroundBox::prepare() {
|
||||
|
||||
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto session = _session;
|
||||
const auto remove = [=, weak = make_weak(this)]{
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
@@ -150,8 +160,8 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
if (weak) {
|
||||
weak->_inner->removePaper(paper);
|
||||
}
|
||||
Auth().data().removeWallpaper(paper);
|
||||
Auth().api().request(MTPaccount_SaveWallPaper(
|
||||
session->data().removeWallpaper(paper);
|
||||
session->api().request(MTPaccount_SaveWallPaper(
|
||||
paper.mtpInput(),
|
||||
MTP_bool(true),
|
||||
paper.mtpSettings()
|
||||
@@ -166,17 +176,21 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent)
|
||||
BackgroundBox::Inner::Inner(
|
||||
QWidget *parent,
|
||||
not_null<Main::Session*> session)
|
||||
: RpWidget(parent)
|
||||
, _session(session)
|
||||
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
|
||||
_check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
|
||||
if (Auth().data().wallpapers().empty()) {
|
||||
if (_session->data().wallpapers().empty()) {
|
||||
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
|
||||
} else {
|
||||
updatePapers();
|
||||
}
|
||||
requestPapers();
|
||||
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
|
||||
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
subscribe(Window::Theme::Background(), [=](const Update &update) {
|
||||
if (update.paletteChanged()) {
|
||||
@@ -192,9 +206,9 @@ BackgroundBox::Inner::Inner(QWidget *parent) : RpWidget(parent)
|
||||
|
||||
void BackgroundBox::Inner::requestPapers() {
|
||||
request(MTPaccount_GetWallPapers(
|
||||
MTP_int(Auth().data().wallpapersHash())
|
||||
MTP_int(_session->data().wallpapersHash())
|
||||
)).done([=](const MTPaccount_WallPapers &result) {
|
||||
if (Auth().data().updateWallpapers(result)) {
|
||||
if (_session->data().updateWallpapers(result)) {
|
||||
updatePapers();
|
||||
}
|
||||
}).send();
|
||||
@@ -220,7 +234,7 @@ void BackgroundBox::Inner::sortPapers() {
|
||||
void BackgroundBox::Inner::updatePapers() {
|
||||
_over = _overDown = Selection();
|
||||
|
||||
_papers = Auth().data().wallpapers(
|
||||
_papers = _session->data().wallpapers(
|
||||
) | ranges::view::filter([](const Data::WallPaper &paper) {
|
||||
return !paper.isPattern() || paper.backgroundColor().has_value();
|
||||
}) | ranges::view::transform([](const Data::WallPaper &paper) {
|
||||
@@ -315,7 +329,7 @@ void BackgroundBox::Inner::paintPaper(
|
||||
if (paper.data.id() == Window::Theme::Background()->id()) {
|
||||
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
|
||||
_check->paint(p, crl::now(), checkLeft, checkTop, width());
|
||||
_check->paint(p, checkLeft, checkTop, width());
|
||||
} else if (Data::IsCloudWallPaper(paper.data)
|
||||
&& !Data::IsDefaultWallPaper(paper.data)
|
||||
&& over.has_value()
|
||||
|
||||
@@ -9,13 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
class WallPaper;
|
||||
} // namespace Data
|
||||
|
||||
class BackgroundBox : public BoxContent {
|
||||
public:
|
||||
BackgroundBox(QWidget*);
|
||||
BackgroundBox(QWidget*, not_null<Main::Session*> session);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -25,6 +29,8 @@ private:
|
||||
|
||||
void removePaper(const Data::WallPaper &paper);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
QPointer<Inner> _inner;
|
||||
|
||||
};
|
||||
|
||||
@@ -16,16 +16,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/view/history_view_message.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_document.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/background_preview_box.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
#include <QtGui/QClipboard>
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxWallPaperSlugLength = 255;
|
||||
@@ -220,7 +224,7 @@ void ServiceCheck::Generator::invalidate() {
|
||||
|
||||
ServiceCheck::Generator &ServiceCheck::Frames() {
|
||||
static const auto Instance = Ui::CreateChild<Generator>(
|
||||
QApplication::instance());
|
||||
QCoreApplication::instance());
|
||||
return *Instance;
|
||||
}
|
||||
|
||||
@@ -283,15 +287,17 @@ AdminLog::OwnedItem GenerateTextItem(
|
||||
const auto flags = Flag::f_entities
|
||||
| Flag::f_from_id
|
||||
| (out ? Flag::f_out : Flag(0));
|
||||
const auto clientFlags = MTPDmessage_ClientFlag::f_fake_history_item;
|
||||
const auto replyTo = 0;
|
||||
const auto viaBotId = 0;
|
||||
const auto item = history->owner().makeMessage(
|
||||
history,
|
||||
++id,
|
||||
flags,
|
||||
clientFlags,
|
||||
replyTo,
|
||||
viaBotId,
|
||||
unixtime(),
|
||||
base::unixtime::now(),
|
||||
out ? history->session().userId() : peerToUser(history->peer->id),
|
||||
QString(),
|
||||
TextWithEntities{ TextUtilities::Clean(text) });
|
||||
@@ -386,20 +392,24 @@ QImage PrepareScaledFromFull(
|
||||
|
||||
BackgroundPreviewBox::BackgroundPreviewBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const Data::WallPaper &paper)
|
||||
: _text1(GenerateTextItem(
|
||||
: _session(session)
|
||||
, _text1(GenerateTextItem(
|
||||
delegate(),
|
||||
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
_session->data().history(
|
||||
peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
tr::lng_background_text1(tr::now),
|
||||
false))
|
||||
, _text2(GenerateTextItem(
|
||||
delegate(),
|
||||
Auth().data().history(peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
_session->data().history(
|
||||
peerFromUser(PeerData::kServiceNotificationsId)),
|
||||
tr::lng_background_text2(tr::now),
|
||||
true))
|
||||
, _paper(paper)
|
||||
, _radial([=](crl::time now) { radialAnimationCallback(now); }) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
|
||||
subscribe(_session->downloaderTaskFinished(), [=] { update(); });
|
||||
}
|
||||
|
||||
not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
|
||||
@@ -482,7 +492,7 @@ void BackgroundPreviewBox::apply() {
|
||||
&& Data::IsCloudWallPaper(_paper);
|
||||
App::main()->setChatBackground(_paper, std::move(_full));
|
||||
if (install) {
|
||||
Auth().api().request(MTPaccount_InstallWallPaper(
|
||||
_session->api().request(MTPaccount_InstallWallPaper(
|
||||
_paper.mtpInput(),
|
||||
_paper.mtpSettings()
|
||||
)).send();
|
||||
@@ -491,7 +501,7 @@ void BackgroundPreviewBox::apply() {
|
||||
}
|
||||
|
||||
void BackgroundPreviewBox::share() {
|
||||
QApplication::clipboard()->setText(_paper.shareUrl());
|
||||
QGuiApplication::clipboard()->setText(_paper.shareUrl());
|
||||
Ui::Toast::Show(tr::lng_background_link_copied(tr::now));
|
||||
}
|
||||
|
||||
@@ -735,18 +745,23 @@ void BackgroundPreviewBox::checkLoadedDocument() {
|
||||
}
|
||||
|
||||
bool BackgroundPreviewBox::Start(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &slug,
|
||||
const QMap<QString, QString> ¶ms) {
|
||||
if (const auto paper = Data::WallPaper::FromColorSlug(slug)) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(paper->withUrlParams(params)));
|
||||
Ui::show(Box<BackgroundPreviewBox>(
|
||||
session,
|
||||
paper->withUrlParams(params)));
|
||||
return true;
|
||||
}
|
||||
if (!IsValidWallPaperSlug(slug)) {
|
||||
Ui::show(Box<InformBox>(tr::lng_background_bad_link(tr::now)));
|
||||
return false;
|
||||
}
|
||||
Auth().api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(result.withUrlParams(params)));
|
||||
session->api().requestWallPaper(slug, [=](const Data::WallPaper &result) {
|
||||
Ui::show(Box<BackgroundPreviewBox>(
|
||||
session,
|
||||
result.withUrlParams(params)));
|
||||
}, [](const RPCError &error) {
|
||||
Ui::show(Box<InformBox>(tr::lng_background_bad_link(tr::now)));
|
||||
});
|
||||
|
||||
@@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class Checkbox;
|
||||
} // namespace Ui
|
||||
@@ -23,9 +27,13 @@ class BackgroundPreviewBox
|
||||
: public BoxContent
|
||||
, private HistoryView::SimpleElementDelegate {
|
||||
public:
|
||||
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
|
||||
BackgroundPreviewBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const Data::WallPaper &paper);
|
||||
|
||||
static bool Start(
|
||||
not_null<Main::Session*> session,
|
||||
const QString &slug,
|
||||
const QMap<QString, QString> ¶ms);
|
||||
|
||||
@@ -58,6 +66,7 @@ private:
|
||||
void startFadeInFrom(QPixmap previous);
|
||||
void checkBlurAnimationStart();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
AdminLog::OwnedItem _text1;
|
||||
AdminLog::OwnedItem _text2;
|
||||
Data::WallPaper _paper;
|
||||
|
||||
@@ -161,6 +161,12 @@ boxPhotoCompressedSkip: 20px;
|
||||
boxPhotoCaptionSkip: 8px;
|
||||
boxPhotoTextFg: windowSubTextFg;
|
||||
|
||||
boxLoadingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||
color: windowSubTextFg;
|
||||
thickness: 2px;
|
||||
}
|
||||
boxLoadingSize: 20px;
|
||||
|
||||
cropPointSize: 10px;
|
||||
cropSkip: 13px;
|
||||
cropMinSize: 20px;
|
||||
@@ -321,12 +327,14 @@ contactsMultiSelect: MultiSelect {
|
||||
}
|
||||
contactsPhotoCheckIcon: icon {{
|
||||
"default_checkbox_check",
|
||||
windowFgActive,
|
||||
overviewCheckFgActive,
|
||||
point(3px, 6px)
|
||||
}};
|
||||
contactsPhotoCheck: RoundCheckbox(defaultRoundCheckbox) {
|
||||
size: 20px;
|
||||
sizeSmall: 0.3;
|
||||
bgInactive: overviewCheckBg;
|
||||
bgActive: overviewCheckBgActive;
|
||||
check: contactsPhotoCheckIcon;
|
||||
}
|
||||
contactsPhotoCheckbox: RoundImageCheckbox {
|
||||
@@ -761,6 +769,7 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
|
||||
textFg: windowActiveTextFg;
|
||||
}
|
||||
rightsUntilMargin: margins(0px, 8px, 0px, 20px);
|
||||
rightsRankMargin: margins(0px, 16px, 0px, 20px);
|
||||
|
||||
mutePhotoButton: UserpicButton(defaultUserpicButton) {
|
||||
size: size(40px, 40px);
|
||||
@@ -876,6 +885,21 @@ themesScroll: ScrollArea(defaultScrollArea) {
|
||||
bottomsh: 0px;
|
||||
topsh: 0px;
|
||||
}
|
||||
themesMenuToggle: IconButton(defaultIconButton) {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
|
||||
icon: icon {{ "title_menu_dots", menuIconFg }};
|
||||
iconOver: icon {{ "title_menu_dots", menuIconFgOver }};
|
||||
iconPosition: point(-1px, -1px);
|
||||
|
||||
rippleAreaPosition: point(4px, 4px);
|
||||
rippleAreaSize: 36px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
themesMenuPosition: point(-2px, 25px);
|
||||
|
||||
createPollField: InputField(defaultInputField) {
|
||||
font: boxTextFont;
|
||||
@@ -988,3 +1012,21 @@ blockUserConfirmation: FlatLabel(boxLabel) {
|
||||
}
|
||||
|
||||
transferCheckWidth: 300px;
|
||||
|
||||
slowmodeLabelsMargin: margins(0px, 5px, 0px, 0px);
|
||||
slowmodeLabel: LabelSimple(defaultLabelSimple) {
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
|
||||
customBadgeField: InputField(defaultInputField) {
|
||||
textMargins: margins(2px, 7px, 2px, 0px);
|
||||
|
||||
placeholderFg: placeholderFg;
|
||||
placeholderFgActive: placeholderFgActive;
|
||||
placeholderFgError: placeholderFgActive;
|
||||
placeholderMargins: margins(0px, 0px, 0px, 0px);
|
||||
placeholderScale: 0.;
|
||||
placeholderFont: normalFont;
|
||||
|
||||
heightMin: 32px;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ public:
|
||||
_month.setForced(_month.value(), true);
|
||||
}
|
||||
|
||||
void setBeginningButton(bool enabled);
|
||||
bool hasBeginningButton() const {
|
||||
return _beginningButton;
|
||||
}
|
||||
|
||||
void setMinDate(QDate date);
|
||||
void setMaxDate(QDate date);
|
||||
|
||||
@@ -55,6 +60,9 @@ public:
|
||||
bool isEnabled(int index) const {
|
||||
return (index >= _minDayIndex) && (index <= _maxDayIndex);
|
||||
}
|
||||
bool atBeginning() const {
|
||||
return _highlighted == _min;
|
||||
}
|
||||
|
||||
const base::Variable<QDate> &month() {
|
||||
return _month;
|
||||
@@ -69,6 +77,8 @@ private:
|
||||
static int daysShiftForMonth(QDate month);
|
||||
static int rowsCountForMonth(QDate month);
|
||||
|
||||
bool _beginningButton = false;
|
||||
|
||||
base::Variable<QDate> _month;
|
||||
QDate _min, _max;
|
||||
QDate _highlighted;
|
||||
@@ -86,6 +96,10 @@ CalendarBox::Context::Context(QDate month, QDate highlighted) : _highlighted(hig
|
||||
showMonth(month);
|
||||
}
|
||||
|
||||
void CalendarBox::Context::setBeginningButton(bool enabled) {
|
||||
_beginningButton = enabled;
|
||||
}
|
||||
|
||||
void CalendarBox::Context::setMinDate(QDate date) {
|
||||
_min = date;
|
||||
applyMonth(_month.value(), true);
|
||||
@@ -192,6 +206,7 @@ public:
|
||||
|
||||
int countHeight();
|
||||
void setDateChosenCallback(Fn<void(QDate)> callback);
|
||||
void selectBeginning();
|
||||
|
||||
~Inner();
|
||||
|
||||
@@ -389,7 +404,10 @@ void CalendarBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
auto pressed = _pressed;
|
||||
setPressed(kEmptySelection);
|
||||
if (pressed != kEmptySelection && pressed == _selected) {
|
||||
_dateChosenCallback(_context->dateFromIndex(pressed));
|
||||
crl::on_main(this, [=] {
|
||||
const auto onstack = _dateChosenCallback;
|
||||
onstack(_context->dateFromIndex(pressed));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +435,10 @@ void CalendarBox::Inner::setDateChosenCallback(Fn<void(QDate)> callback) {
|
||||
_dateChosenCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void CalendarBox::Inner::selectBeginning() {
|
||||
_dateChosenCallback(_context->dateFromIndex(_context->minDayIndex()));
|
||||
}
|
||||
|
||||
CalendarBox::Inner::~Inner() = default;
|
||||
|
||||
class CalendarBox::Title : public TWidget, private base::Subscriber {
|
||||
@@ -494,6 +516,14 @@ void CalendarBox::setMaxDate(QDate date) {
|
||||
_context->setMaxDate(date);
|
||||
}
|
||||
|
||||
bool CalendarBox::hasBeginningButton() const {
|
||||
return _context->hasBeginningButton();
|
||||
}
|
||||
|
||||
void CalendarBox::setBeginningButton(bool enabled) {
|
||||
_context->setBeginningButton(enabled);
|
||||
}
|
||||
|
||||
void CalendarBox::prepare() {
|
||||
_previous->setClickedCallback([this] { goPreviousMonth(); });
|
||||
_next->setClickedCallback([this] { goNextMonth(); });
|
||||
@@ -510,6 +540,9 @@ void CalendarBox::prepare() {
|
||||
if (_finalize) {
|
||||
_finalize(this);
|
||||
}
|
||||
if (!_context->atBeginning() && hasBeginningButton()) {
|
||||
addLeftButton(tr::lng_calendar_beginning(), [this] { _inner->selectBeginning(); });
|
||||
}
|
||||
}
|
||||
|
||||
bool CalendarBox::isPreviousEnabled() const {
|
||||
@@ -555,6 +588,8 @@ void CalendarBox::resizeEvent(QResizeEvent *e) {
|
||||
void CalendarBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape) {
|
||||
e->ignore();
|
||||
} else if (e->key() == Qt::Key_Home) {
|
||||
_inner->selectBeginning();
|
||||
} else if (e->key() == Qt::Key_Left) {
|
||||
goPreviousMonth();
|
||||
} else if (e->key() == Qt::Key_Right) {
|
||||
|
||||
@@ -33,6 +33,8 @@ public:
|
||||
FnMut<void(not_null<CalendarBox*>)> finalize,
|
||||
const style::CalendarSizes &st);
|
||||
|
||||
void setBeginningButton(bool enabled);
|
||||
bool hasBeginningButton() const;
|
||||
|
||||
void setMinDate(QDate date);
|
||||
void setMaxDate(QDate date);
|
||||
|
||||
@@ -15,8 +15,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "boxes/confirm_phone_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
namespace {
|
||||
@@ -60,8 +61,7 @@ void createErrorLabel(
|
||||
|
||||
class ChangePhoneBox::EnterPhone : public BoxContent {
|
||||
public:
|
||||
EnterPhone(QWidget*) {
|
||||
}
|
||||
EnterPhone(QWidget*, not_null<Main::Session*> session);
|
||||
|
||||
void setInnerFocus() override {
|
||||
_phone->setFocusFast();
|
||||
@@ -79,6 +79,8 @@ private:
|
||||
showError(QString());
|
||||
}
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
object_ptr<Ui::PhoneInput> _phone = { nullptr };
|
||||
object_ptr<Ui::FadeWrap<Ui::FlatLabel>> _error = { nullptr };
|
||||
mtpRequestId _requestId = 0;
|
||||
@@ -87,7 +89,13 @@ private:
|
||||
|
||||
class ChangePhoneBox::EnterCode : public BoxContent {
|
||||
public:
|
||||
EnterCode(QWidget*, const QString &phone, const QString &hash, int codeLength, int callTimeout);
|
||||
EnterCode(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const QString &phone,
|
||||
const QString &hash,
|
||||
int codeLength,
|
||||
int callTimeout);
|
||||
|
||||
void setInnerFocus() override {
|
||||
_code->setFocusFast();
|
||||
@@ -107,6 +115,8 @@ private:
|
||||
}
|
||||
int countHeight();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
QString _phone;
|
||||
QString _hash;
|
||||
int _codeLength = 0;
|
||||
@@ -119,11 +129,22 @@ private:
|
||||
|
||||
};
|
||||
|
||||
ChangePhoneBox::EnterPhone::EnterPhone(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
void ChangePhoneBox::EnterPhone::prepare() {
|
||||
setTitle(tr::lng_change_phone_title());
|
||||
|
||||
auto phoneValue = QString();
|
||||
_phone.create(this, st::defaultInputField, tr::lng_change_phone_new_title(), phoneValue);
|
||||
_phone.create(
|
||||
this,
|
||||
st::defaultInputField,
|
||||
tr::lng_change_phone_new_title(),
|
||||
ExtractPhonePrefix(_session->user()->phone()),
|
||||
phoneValue);
|
||||
|
||||
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
|
||||
_phone->moveToLeft(st::boxPadding.left(), st::boxLittleSkip);
|
||||
@@ -149,9 +170,7 @@ void ChangePhoneBox::EnterPhone::submit() {
|
||||
_requestId = MTP::send(
|
||||
MTPaccount_SendChangePhoneCode(
|
||||
MTP_string(phoneNumber),
|
||||
MTP_codeSettings(
|
||||
MTP_flags(0),
|
||||
MTPstring())),
|
||||
MTP_codeSettings(MTP_flags(0))),
|
||||
rpcDone(crl::guard(this, [=](
|
||||
const MTPauth_SentCode &result) {
|
||||
return sendPhoneDone(phoneNumber, result);
|
||||
@@ -188,6 +207,7 @@ void ChangePhoneBox::EnterPhone::sendPhoneDone(const QString &phoneNumber, const
|
||||
}
|
||||
Ui::show(
|
||||
Box<EnterCode>(
|
||||
_session,
|
||||
phoneNumber,
|
||||
phoneCodeHash,
|
||||
codeLength,
|
||||
@@ -229,8 +249,15 @@ void ChangePhoneBox::EnterPhone::showError(const QString &text) {
|
||||
}
|
||||
}
|
||||
|
||||
ChangePhoneBox::EnterCode::EnterCode(QWidget*, const QString &phone, const QString &hash, int codeLength, int callTimeout)
|
||||
: _phone(phone)
|
||||
ChangePhoneBox::EnterCode::EnterCode(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const QString &phone,
|
||||
const QString &hash,
|
||||
int codeLength,
|
||||
int callTimeout)
|
||||
: _session(session)
|
||||
, _phone(phone)
|
||||
, _hash(hash)
|
||||
, _codeLength(codeLength)
|
||||
, _callTimeout(callTimeout)
|
||||
@@ -279,13 +306,15 @@ void ChangePhoneBox::EnterCode::submit() {
|
||||
}
|
||||
hideError();
|
||||
|
||||
const auto session = _session;
|
||||
const auto code = _code->getDigitsOnly();
|
||||
const auto weak = make_weak(this);
|
||||
_requestId = MTP::send(MTPaccount_ChangePhone(
|
||||
MTP_string(_phone),
|
||||
MTP_string(_hash),
|
||||
MTP_string(code)
|
||||
), rpcDone([weak = make_weak(this)](const MTPUser &result) {
|
||||
Auth().data().processUser(result);
|
||||
), rpcDone([=](const MTPUser &result) {
|
||||
session->data().processUser(result);
|
||||
if (weak) {
|
||||
Ui::hideLayer();
|
||||
}
|
||||
@@ -342,11 +371,17 @@ bool ChangePhoneBox::EnterCode::sendCodeFail(const RPCError &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ChangePhoneBox::ChangePhoneBox(QWidget*, not_null<Main::Session*> session)
|
||||
: _session(session) {
|
||||
}
|
||||
|
||||
void ChangePhoneBox::prepare() {
|
||||
const auto session = _session;
|
||||
|
||||
setTitle(tr::lng_change_phone_title());
|
||||
addButton(tr::lng_change_phone_button(), [] {
|
||||
Ui::show(Box<ConfirmBox>(tr::lng_change_phone_warning(tr::now), [] {
|
||||
Ui::show(Box<EnterPhone>());
|
||||
addButton(tr::lng_change_phone_button(), [=] {
|
||||
Ui::show(Box<ConfirmBox>(tr::lng_change_phone_warning(tr::now), [=] {
|
||||
Ui::show(Box<EnterPhone>(session));
|
||||
}));
|
||||
});
|
||||
addButton(tr::lng_cancel(), [this] {
|
||||
|
||||
@@ -9,10 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
class ChangePhoneBox : public BoxContent {
|
||||
public:
|
||||
ChangePhoneBox(QWidget*) {
|
||||
}
|
||||
ChangePhoneBox(QWidget*, not_null<Main::Session*> session);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -23,5 +26,7 @@ private:
|
||||
class EnterPhone;
|
||||
class EnterCode;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -25,14 +25,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/click_handler_types.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "auth_session.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "main/main_session.h"
|
||||
#include "observer_peer.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
TextParseOptions _confirmBoxTextOptions = {
|
||||
TextParseLinks | TextParseMultiline | TextParseMarkdown | TextParseRichText, // flags
|
||||
0, // maxw
|
||||
@@ -458,7 +463,8 @@ DeleteMessagesBox::DeleteMessagesBox(
|
||||
QWidget*,
|
||||
not_null<HistoryItem*> item,
|
||||
bool suggestModerateActions)
|
||||
: _ids(1, item->fullId()) {
|
||||
: _session(&item->history()->session())
|
||||
, _ids(1, item->fullId()) {
|
||||
if (suggestModerateActions) {
|
||||
_moderateBan = item->suggestBanReport();
|
||||
_moderateDeleteAll = item->suggestDeleteAllReport();
|
||||
@@ -471,8 +477,10 @@ DeleteMessagesBox::DeleteMessagesBox(
|
||||
|
||||
DeleteMessagesBox::DeleteMessagesBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
MessageIdsList &&selected)
|
||||
: _ids(std::move(selected)) {
|
||||
: _session(session)
|
||||
, _ids(std::move(selected)) {
|
||||
Expects(!_ids.empty());
|
||||
}
|
||||
|
||||
@@ -480,7 +488,8 @@ DeleteMessagesBox::DeleteMessagesBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
bool justClear)
|
||||
: _wipeHistoryPeer(peer)
|
||||
: _session(&peer->session())
|
||||
, _wipeHistoryPeer(peer)
|
||||
, _wipeHistoryJustClear(justClear) {
|
||||
}
|
||||
|
||||
@@ -537,10 +546,11 @@ void DeleteMessagesBox::prepare() {
|
||||
: tr::lng_selected_delete_sure(tr::now, lt_count, _ids.size());
|
||||
if (const auto peer = checkFromSinglePeer()) {
|
||||
auto count = int(_ids.size());
|
||||
if (auto revoke = revokeText(peer)) {
|
||||
if (hasScheduledMessages()) {
|
||||
} else if (auto revoke = revokeText(peer)) {
|
||||
_revoke.create(this, revoke->checkbox, false, st::defaultBoxCheckbox);
|
||||
appendDetails(std::move(revoke->description));
|
||||
} else if (peer && peer->isChannel()) {
|
||||
} else if (peer->isChannel()) {
|
||||
if (peer->isMegagroup()) {
|
||||
appendDetails({ tr::lng_delete_for_everyone_hint(tr::now, lt_count, count) });
|
||||
}
|
||||
@@ -575,10 +585,21 @@ void DeleteMessagesBox::prepare() {
|
||||
setDimensions(st::boxWidth, fullHeight);
|
||||
}
|
||||
|
||||
bool DeleteMessagesBox::hasScheduledMessages() const {
|
||||
for (const auto fullId : std::as_const(_ids)) {
|
||||
if (const auto item = _session->data().message(fullId)) {
|
||||
if (item->isScheduled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PeerData *DeleteMessagesBox::checkFromSinglePeer() const {
|
||||
auto result = (PeerData*)nullptr;
|
||||
for (const auto fullId : std::as_const(_ids)) {
|
||||
if (const auto item = Auth().data().message(fullId)) {
|
||||
if (const auto item = _session->data().message(fullId)) {
|
||||
const auto peer = item->history()->peer;
|
||||
if (!result) {
|
||||
result = peer;
|
||||
@@ -620,7 +641,7 @@ auto DeleteMessagesBox::revokeText(not_null<PeerData*> peer) const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto now = unixtime();
|
||||
const auto now = base::unixtime::now();
|
||||
const auto canRevoke = [&](HistoryItem * item) {
|
||||
return item->canDeleteForEveryone(now);
|
||||
};
|
||||
@@ -760,9 +781,21 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
}
|
||||
|
||||
base::flat_map<not_null<PeerData*>, QVector<MTPint>> idsByPeer;
|
||||
base::flat_map<not_null<PeerData*>, QVector<MTPint>> scheduledIdsByPeer;
|
||||
for (const auto itemId : _ids) {
|
||||
if (const auto item = Auth().data().message(itemId)) {
|
||||
if (const auto item = _session->data().message(itemId)) {
|
||||
const auto history = item->history();
|
||||
if (item->isScheduled()) {
|
||||
const auto wasOnServer = !item->isSending()
|
||||
&& !item->hasFailed();
|
||||
if (wasOnServer) {
|
||||
scheduledIdsByPeer[history->peer].push_back(MTP_int(
|
||||
_session->data().scheduledMessages().lookupId(item)));
|
||||
} else {
|
||||
_session->data().scheduledMessages().removeSending(item);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const auto wasOnServer = IsServerMsgId(item->id);
|
||||
const auto wasLast = (history->lastMessage() == item);
|
||||
const auto wasInChats = (history->chatListMessage() == item);
|
||||
@@ -779,18 +812,30 @@ void DeleteMessagesBox::deleteAndClear() {
|
||||
for (const auto &[peer, ids] : idsByPeer) {
|
||||
peer->session().api().deleteMessages(peer, ids, revoke);
|
||||
}
|
||||
for (const auto &[peer, ids] : scheduledIdsByPeer) {
|
||||
peer->session().api().request(MTPmessages_DeleteScheduledMessages(
|
||||
peer->input,
|
||||
MTP_vector<MTPint>(ids)
|
||||
)).done([=, peer=peer](const MTPUpdates &updates) {
|
||||
peer->session().api().applyUpdates(updates);
|
||||
}).send();
|
||||
}
|
||||
|
||||
const auto session = _session;
|
||||
Ui::hideLayer();
|
||||
Auth().data().sendHistoryChangeNotifications();
|
||||
session->data().sendHistoryChangeNotifications();
|
||||
}
|
||||
|
||||
ConfirmInviteBox::ConfirmInviteBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data,
|
||||
Fn<void()> submit)
|
||||
: _submit(std::move(submit))
|
||||
: _session(session)
|
||||
, _submit(std::move(submit))
|
||||
, _title(this, st::confirmInviteTitle)
|
||||
, _status(this, st::confirmInviteStatus)
|
||||
, _participants(GetParticipants(data))
|
||||
, _participants(GetParticipants(_session, data))
|
||||
, _isChannel(data.is_channel() && !data.is_megagroup()) {
|
||||
const auto title = qs(data.vtitle());
|
||||
const auto count = data.vparticipants_count().v;
|
||||
@@ -806,11 +851,11 @@ ConfirmInviteBox::ConfirmInviteBox(
|
||||
_title->setText(title);
|
||||
_status->setText(status);
|
||||
|
||||
const auto photo = Auth().data().processPhoto(data.vphoto());
|
||||
const auto photo = _session->data().processPhoto(data.vphoto());
|
||||
if (!photo->isNull()) {
|
||||
_photo = photo->thumbnail();
|
||||
if (!_photo->loaded()) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] {
|
||||
subscribe(_session->downloaderTaskFinished(), [=] {
|
||||
update();
|
||||
});
|
||||
_photo->load(Data::FileOrigin());
|
||||
@@ -823,6 +868,7 @@ ConfirmInviteBox::ConfirmInviteBox(
|
||||
}
|
||||
|
||||
std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data) {
|
||||
const auto participants = data.vparticipants();
|
||||
if (!participants) {
|
||||
@@ -832,7 +878,7 @@ std::vector<not_null<UserData*>> ConfirmInviteBox::GetParticipants(
|
||||
auto result = std::vector<not_null<UserData*>>();
|
||||
result.reserve(v.size());
|
||||
for (const auto &participant : v) {
|
||||
if (const auto user = Auth().data().processUser(participant)) {
|
||||
if (const auto user = session->data().processUser(participant)) {
|
||||
result.push_back(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class Checkbox;
|
||||
class FlatLabel;
|
||||
@@ -150,7 +154,10 @@ public:
|
||||
QWidget*,
|
||||
not_null<HistoryItem*> item,
|
||||
bool suggestModerateActions);
|
||||
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
|
||||
DeleteMessagesBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
MessageIdsList &&selected);
|
||||
DeleteMessagesBox(QWidget*, not_null<PeerData*> peer, bool justClear);
|
||||
|
||||
void setDeleteConfirmedCallback(Fn<void()> callback) {
|
||||
@@ -169,8 +176,12 @@ private:
|
||||
TextWithEntities description;
|
||||
};
|
||||
void deleteAndClear();
|
||||
PeerData *checkFromSinglePeer() const;
|
||||
std::optional<RevokeConfig> revokeText(not_null<PeerData*> peer) const;
|
||||
[[nodiscard]] PeerData *checkFromSinglePeer() const;
|
||||
[[nodiscard]] bool hasScheduledMessages() const;
|
||||
[[nodiscard]] std::optional<RevokeConfig> revokeText(
|
||||
not_null<PeerData*> peer) const;
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
PeerData * const _wipeHistoryPeer = nullptr;
|
||||
const bool _wipeHistoryJustClear = false;
|
||||
@@ -194,6 +205,7 @@ class ConfirmInviteBox : public BoxContent, public RPCSender {
|
||||
public:
|
||||
ConfirmInviteBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data,
|
||||
Fn<void()> submit);
|
||||
~ConfirmInviteBox();
|
||||
@@ -206,8 +218,11 @@ protected:
|
||||
|
||||
private:
|
||||
static std::vector<not_null<UserData*>> GetParticipants(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPDchatInvite &data);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
Fn<void()> _submit;
|
||||
object_ptr<Ui::FlatLabel> _title;
|
||||
object_ptr<Ui::FlatLabel> _status;
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/qthelp_url.h" // qthelp::url_encode
|
||||
#include "platform/platform_info.h" // Platform::SystemVersionPretty
|
||||
#include "mainwidget.h"
|
||||
#include "numbers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace {
|
||||
@@ -64,7 +65,14 @@ void ShowPhoneBannedError(const QString &phone) {
|
||||
tr::lng_signin_banned_help(tr::now),
|
||||
close,
|
||||
[=] { SendToBannedHelp(phone); close(); }));
|
||||
}
|
||||
|
||||
QString ExtractPhonePrefix(const QString &phone) {
|
||||
const auto pattern = phoneNumberParse(phone);
|
||||
if (!pattern.isEmpty()) {
|
||||
return phone.mid(0, pattern[0]);
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
SentCodeField::SentCodeField(
|
||||
@@ -219,9 +227,7 @@ void ConfirmPhoneBox::checkPhoneAndHash() {
|
||||
_sendCodeRequestId = MTP::send(
|
||||
MTPaccount_SendConfirmPhoneCode(
|
||||
MTP_string(_hash),
|
||||
MTP_codeSettings(
|
||||
MTP_flags(0),
|
||||
MTPstring())),
|
||||
MTP_codeSettings(MTP_flags(0))),
|
||||
rpcDone(&ConfirmPhoneBox::sendCodeDone),
|
||||
rpcFail(&ConfirmPhoneBox::sendCodeFail));
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
void ShowPhoneBannedError(const QString &phone);
|
||||
[[nodiscard]] QString ExtractPhonePrefix(const QString &phone);
|
||||
|
||||
class SentCodeField : public Ui::InputField {
|
||||
public:
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/localstorage.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "core/application.h"
|
||||
#include "main/main_account.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -27,10 +28,64 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_chat_helpers.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000);
|
||||
|
||||
class Base64UrlInput : public Ui::MaskedInputField {
|
||||
public:
|
||||
Base64UrlInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val);
|
||||
|
||||
protected:
|
||||
void correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) override;
|
||||
|
||||
};
|
||||
|
||||
Base64UrlInput::Base64UrlInput(
|
||||
QWidget *parent,
|
||||
const style::InputField &st,
|
||||
rpl::producer<QString> placeholder,
|
||||
const QString &val)
|
||||
: MaskedInputField(parent, st, std::move(placeholder), val) {
|
||||
if (!QRegularExpression("^[a-zA-Z0-9_\\-]+$").match(val).hasMatch()) {
|
||||
setText(QString());
|
||||
}
|
||||
}
|
||||
|
||||
void Base64UrlInput::correctValue(
|
||||
const QString &was,
|
||||
int wasCursor,
|
||||
QString &now,
|
||||
int &nowCursor) {
|
||||
QString newText;
|
||||
newText.reserve(now.size());
|
||||
auto newPos = nowCursor;
|
||||
for (auto i = 0, l = now.size(); i < l; ++i) {
|
||||
const auto ch = now[i];
|
||||
if ((ch >= '0' && ch <= '9')
|
||||
|| (ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch == '-')
|
||||
|| (ch == '_')) {
|
||||
newText.append(ch);
|
||||
} else if (i < nowCursor) {
|
||||
--newPos;
|
||||
}
|
||||
}
|
||||
setCorrectedText(now, nowCursor, newText, newPos);
|
||||
}
|
||||
|
||||
class ProxyRow : public Ui::RippleButton {
|
||||
public:
|
||||
using View = ProxiesBoxController::ItemView;
|
||||
@@ -150,7 +205,7 @@ private:
|
||||
QPointer<Ui::PortInput> _port;
|
||||
QPointer<Ui::InputField> _user;
|
||||
QPointer<Ui::PasswordInput> _password;
|
||||
QPointer<Ui::HexInput> _secret;
|
||||
QPointer<Base64UrlInput> _secret;
|
||||
|
||||
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _credentials;
|
||||
QPointer<Ui::SlideWrap<Ui::VerticalLayout>> _mtprotoCredentials;
|
||||
@@ -852,12 +907,11 @@ void ProxyBox::setupMtprotoCredentials(const ProxyData &data) {
|
||||
addLabel(mtproto, tr::lng_proxy_credentials(tr::now));
|
||||
|
||||
auto secretWrap = object_ptr<Ui::RpWidget>(mtproto);
|
||||
_secret = Ui::CreateChild<Ui::HexInput>(
|
||||
_secret = Ui::CreateChild<Base64UrlInput>(
|
||||
secretWrap.data(),
|
||||
st::connectionUserInputField,
|
||||
tr::lng_connection_proxy_secret_ph(),
|
||||
(data.type == Type::Mtproto) ? data.password : QString());
|
||||
_secret->setMaxLength(ProxyData::MaxMtprotoPasswordLength());
|
||||
_secret->move(0, 0);
|
||||
_secret->heightValue(
|
||||
) | rpl::start_with_next([=, wrap = secretWrap.data()](int height) {
|
||||
@@ -975,6 +1029,11 @@ void ProxiesBoxController::ShowApplyConfirmation(
|
||||
strong->closeBox();
|
||||
}
|
||||
}), LayerOption::KeepOther);
|
||||
} else {
|
||||
Ui::show(Box<InformBox>(
|
||||
(proxy.status() == ProxyData::Status::Unsupported
|
||||
? tr::lng_proxy_unsupported(tr::now)
|
||||
: tr::lng_proxy_invalid(tr::now))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -990,58 +1049,55 @@ void ProxiesBoxController::refreshChecker(Item &item) {
|
||||
const auto type = (item.data.type == Type::Http)
|
||||
? Variants::Http
|
||||
: Variants::Tcp;
|
||||
const auto mtproto = Core::App().mtp();
|
||||
const auto mtproto = Core::App().activeAccount().mtp();
|
||||
const auto dcId = mtproto->mainDcId();
|
||||
|
||||
item.state = ItemState::Checking;
|
||||
const auto setup = [&](Checker &checker) {
|
||||
const auto setup = [&](Checker &checker, const bytes::vector &secret) {
|
||||
checker = MTP::internal::AbstractConnection::Create(
|
||||
mtproto,
|
||||
type,
|
||||
QThread::currentThread(),
|
||||
secret,
|
||||
item.data);
|
||||
setupChecker(item.id, checker);
|
||||
};
|
||||
setup(item.checker);
|
||||
if (item.data.type == Type::Mtproto) {
|
||||
item.checkerv6 = nullptr;
|
||||
const auto secret = item.data.secretFromMtprotoPassword();
|
||||
setup(item.checker, secret);
|
||||
item.checker->connectToServer(
|
||||
item.data.host,
|
||||
item.data.port,
|
||||
item.data.secretFromMtprotoPassword(),
|
||||
secret,
|
||||
dcId);
|
||||
item.checkerv6 = nullptr;
|
||||
} else {
|
||||
const auto options = mtproto->dcOptions()->lookup(
|
||||
dcId,
|
||||
MTP::DcType::Regular,
|
||||
true);
|
||||
const auto endpoint = options.data[Variants::IPv4][type];
|
||||
const auto endpointv6 = options.data[Variants::IPv6][type];
|
||||
if (endpoint.empty()) {
|
||||
item.checker = nullptr;
|
||||
}
|
||||
if (Global::TryIPv6() && !endpointv6.empty()) {
|
||||
setup(item.checkerv6);
|
||||
} else {
|
||||
item.checkerv6 = nullptr;
|
||||
}
|
||||
const auto connect = [&](
|
||||
Checker &checker,
|
||||
Variants::Address address) {
|
||||
const auto &list = options.data[address][type];
|
||||
if (list.empty()
|
||||
|| (address == Variants::IPv6 && !Global::TryIPv6())) {
|
||||
checker = nullptr;
|
||||
return;
|
||||
}
|
||||
const auto &endpoint = list.front();
|
||||
setup(checker, endpoint.secret);
|
||||
checker->connectToServer(
|
||||
QString::fromStdString(endpoint.ip),
|
||||
endpoint.port,
|
||||
endpoint.secret,
|
||||
dcId);
|
||||
};
|
||||
connect(item.checker, Variants::IPv4);
|
||||
connect(item.checkerv6, Variants::IPv6);
|
||||
if (!item.checker && !item.checkerv6) {
|
||||
item.state = ItemState::Unavailable;
|
||||
return;
|
||||
}
|
||||
const auto connect = [&](
|
||||
const Checker &checker,
|
||||
const std::vector<MTP::DcOptions::Endpoint> &endpoints) {
|
||||
if (checker) {
|
||||
checker->connectToServer(
|
||||
QString::fromStdString(endpoints.front().ip),
|
||||
endpoints.front().port,
|
||||
endpoints.front().secret,
|
||||
dcId);
|
||||
}
|
||||
};
|
||||
connect(item.checker, endpoint);
|
||||
connect(item.checkerv6, endpointv6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1373,7 +1429,7 @@ void ProxiesBoxController::share(const ProxyData &proxy) {
|
||||
? "&pass=" + qthelp::url_encode(proxy.password) : "")
|
||||
+ ((proxy.type == Type::Mtproto && !proxy.password.isEmpty())
|
||||
? "&secret=" + proxy.password : "");
|
||||
QApplication::clipboard()->setText(link);
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
|
||||
@@ -1381,7 +1437,7 @@ ProxiesBoxController::~ProxiesBoxController() {
|
||||
if (_saveTimer.isActive()) {
|
||||
App::CallDelayed(
|
||||
kSaveSettingsDelayedTimeout,
|
||||
QApplication::instance(),
|
||||
QCoreApplication::instance(),
|
||||
[] { Local::writeSettings(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "main/main_session.h"
|
||||
#include "core/event_filter.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "base/unique_qptr.h"
|
||||
#include "styles/style_boxes.h"
|
||||
@@ -36,7 +39,8 @@ class Options {
|
||||
public:
|
||||
Options(
|
||||
not_null<QWidget*> outer,
|
||||
not_null<Ui::VerticalLayout*> container);
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session);
|
||||
|
||||
[[nodiscard]] bool isValid() const;
|
||||
[[nodiscard]] rpl::producer<bool> isValidChanged() const;
|
||||
@@ -53,6 +57,7 @@ private:
|
||||
static Option Create(
|
||||
not_null<QWidget*> outer,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
int position);
|
||||
|
||||
void toggleRemoveAlways(bool toggled);
|
||||
@@ -122,6 +127,7 @@ private:
|
||||
|
||||
not_null<QWidget*> _outer;
|
||||
not_null<Ui::VerticalLayout*> _container;
|
||||
const not_null<Main::Session*> _session;
|
||||
int _position = 0;
|
||||
std::vector<Option> _list;
|
||||
std::set<Option, std::less<>> _destroyed;
|
||||
@@ -134,12 +140,18 @@ private:
|
||||
|
||||
void InitField(
|
||||
not_null<QWidget*> container,
|
||||
not_null<Ui::InputField*> field) {
|
||||
not_null<Ui::InputField*> field,
|
||||
not_null<Main::Session*> session) {
|
||||
field->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
field->setInstantReplacesEnabled(
|
||||
session->settings().replaceEmojiValue());
|
||||
auto options = Ui::Emoji::SuggestionsController::Options();
|
||||
options.suggestExactFirstWord = false;
|
||||
Ui::Emoji::SuggestionsController::Init(container, field, options);
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
container,
|
||||
field,
|
||||
session,
|
||||
options);
|
||||
}
|
||||
|
||||
not_null<Ui::FlatLabel*> CreateWarningLabel(
|
||||
@@ -176,6 +188,7 @@ void FocusAtEnd(not_null<Ui::InputField*> field) {
|
||||
Options::Option Options::Option::Create(
|
||||
not_null<QWidget*> outer,
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session,
|
||||
int position) {
|
||||
auto result = Option();
|
||||
const auto field = container->insert(
|
||||
@@ -187,7 +200,7 @@ Options::Option Options::Option::Create(
|
||||
st::createPollOptionField,
|
||||
Ui::InputField::Mode::NoNewlines,
|
||||
tr::lng_polls_create_option_add())));
|
||||
InitField(outer, field->entity());
|
||||
InitField(outer, field->entity(), session);
|
||||
field->entity()->setMaxLength(kOptionLimit + kErrorLimit);
|
||||
result._field.reset(field);
|
||||
|
||||
@@ -341,9 +354,11 @@ rpl::producer<Qt::MouseButton> Options::Option::removeClicks() const {
|
||||
|
||||
Options::Options(
|
||||
not_null<QWidget*> outer,
|
||||
not_null<Ui::VerticalLayout*> container)
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<Main::Session*> session)
|
||||
: _outer(outer)
|
||||
, _container(container)
|
||||
, _session(session)
|
||||
, _position(_container->count()) {
|
||||
checkLastOption();
|
||||
}
|
||||
@@ -488,6 +503,7 @@ void Options::addEmptyOption() {
|
||||
_list.push_back(Option::Create(
|
||||
_outer,
|
||||
_container,
|
||||
_session,
|
||||
_position + _list.size() + _destroyed.size()));
|
||||
const auto field = _list.back().field();
|
||||
QObject::connect(field, &Ui::InputField::submitted, [=] {
|
||||
@@ -507,11 +523,11 @@ void Options::addEmptyOption() {
|
||||
Core::InstallEventFilter(field, [=](not_null<QEvent*> event) {
|
||||
if (event->type() != QEvent::KeyPress
|
||||
|| !field->getLastText().isEmpty()) {
|
||||
return false;
|
||||
return Core::EventFilter::Result::Continue;
|
||||
}
|
||||
const auto key = static_cast<QKeyEvent*>(event.get())->key();
|
||||
if (key != Qt::Key_Backspace) {
|
||||
return false;
|
||||
return Core::EventFilter::Result::Continue;
|
||||
}
|
||||
|
||||
const auto index = findField(field);
|
||||
@@ -520,7 +536,7 @@ void Options::addEmptyOption() {
|
||||
} else {
|
||||
_backspaceInFront.fire({});
|
||||
}
|
||||
return true;
|
||||
return Core::EventFilter::Result::Cancel;
|
||||
});
|
||||
|
||||
_list.back().removeClicks(
|
||||
@@ -578,10 +594,15 @@ void Options::checkLastOption() {
|
||||
|
||||
} // namespace
|
||||
|
||||
CreatePollBox::CreatePollBox(QWidget*) {
|
||||
CreatePollBox::CreatePollBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Api::SendType sendType)
|
||||
: _session(session)
|
||||
, _sendType(sendType) {
|
||||
}
|
||||
|
||||
rpl::producer<PollData> CreatePollBox::submitRequests() const {
|
||||
rpl::producer<CreatePollBox::Result> CreatePollBox::submitRequests() const {
|
||||
return _submitRequests.events();
|
||||
}
|
||||
|
||||
@@ -605,7 +626,7 @@ not_null<Ui::InputField*> CreatePollBox::setupQuestion(
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
tr::lng_polls_create_question_placeholder()),
|
||||
st::createPollFieldPadding);
|
||||
InitField(getDelegate()->outerContainer(), question);
|
||||
InitField(getDelegate()->outerContainer(), question, _session);
|
||||
question->setMaxLength(kQuestionLimit + kErrorLimit);
|
||||
|
||||
const auto warning = CreateWarningLabel(
|
||||
@@ -648,7 +669,8 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
AddSubsectionTitle(container, tr::lng_polls_create_options());
|
||||
const auto options = lifetime().make_state<Options>(
|
||||
getDelegate()->outerContainer(),
|
||||
container);
|
||||
container,
|
||||
_session);
|
||||
auto limit = options->usedCount() | rpl::after_next([=](int count) {
|
||||
setCloseByEscape(!count);
|
||||
setCloseByOutsideClick(!count);
|
||||
@@ -687,6 +709,22 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
result.answers = options->toPollAnswers();
|
||||
return result;
|
||||
};
|
||||
const auto send = [=](Api::SendOptions options) {
|
||||
_submitRequests.fire({ collectResult(), options });
|
||||
};
|
||||
const auto sendSilent = [=] {
|
||||
auto options = Api::SendOptions();
|
||||
options.silent = true;
|
||||
send(options);
|
||||
};
|
||||
const auto sendScheduled = [=] {
|
||||
Ui::show(
|
||||
HistoryView::PrepareScheduleBox(
|
||||
this,
|
||||
SendMenuType::Scheduled,
|
||||
send),
|
||||
LayerOption::KeepOther);
|
||||
};
|
||||
const auto updateValid = [=] {
|
||||
valid->fire(isValidQuestion() && options->isValid());
|
||||
};
|
||||
@@ -699,9 +737,16 @@ object_ptr<Ui::RpWidget> CreatePollBox::setupContent() {
|
||||
) | rpl::start_with_next([=](bool valid) {
|
||||
clearButtons();
|
||||
if (valid) {
|
||||
addButton(
|
||||
const auto submit = addButton(
|
||||
tr::lng_polls_create_button(),
|
||||
[=] { _submitRequests.fire(collectResult()); });
|
||||
[=] { send({}); });
|
||||
if (_sendType == Api::SendType::Normal) {
|
||||
SetupSendMenu(
|
||||
submit.data(),
|
||||
[=] { return SendMenuType::Scheduled; },
|
||||
sendSilent,
|
||||
sendScheduled);
|
||||
}
|
||||
}
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
}, lifetime());
|
||||
|
||||
@@ -8,18 +8,32 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "api/api_common.h"
|
||||
#include "data/data_poll.h"
|
||||
|
||||
struct PollData;
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
struct PollData;
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
class CreatePollBox : public BoxContent {
|
||||
public:
|
||||
CreatePollBox(QWidget*);
|
||||
struct Result {
|
||||
PollData poll;
|
||||
Api::SendOptions options;
|
||||
};
|
||||
|
||||
rpl::producer<PollData> submitRequests() const;
|
||||
CreatePollBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
Api::SendType sendType);
|
||||
|
||||
rpl::producer<Result> submitRequests() const;
|
||||
void submitFailed(const QString &error);
|
||||
|
||||
void setInnerFocus() override;
|
||||
@@ -32,8 +46,10 @@ private:
|
||||
not_null<Ui::InputField*> setupQuestion(
|
||||
not_null<Ui::VerticalLayout*> container);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
const Api::SendType _sendType = Api::SendType();
|
||||
Fn<void()> _setInnerFocus;
|
||||
Fn<rpl::producer<bool>()> _dataIsValidValue;
|
||||
rpl::event_stream<PollData> _submitRequests;
|
||||
rpl::event_stream<Result> _submitRequests;
|
||||
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/edit_caption_box.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "chat_helpers/tabbed_panel.h"
|
||||
@@ -38,6 +38,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "confirm_box.h"
|
||||
|
||||
#include <QtCore/QMimeData>
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> controller,
|
||||
@@ -232,7 +234,7 @@ EditCaptionBox::EditCaptionBox(
|
||||
_thumbnailImageLoaded = _thumbnailImage
|
||||
? _thumbnailImage->loaded()
|
||||
: true;
|
||||
subscribe(Auth().downloaderTaskFinished(), [=] {
|
||||
subscribe(_controller->session().downloaderTaskFinished(), [=] {
|
||||
if (!_thumbnailImageLoaded
|
||||
&& _thumbnailImage
|
||||
&& _thumbnailImage->loaded()) {
|
||||
@@ -254,9 +256,11 @@ EditCaptionBox::EditCaptionBox(
|
||||
_field->setMaxLength(Global::CaptionLengthMax());
|
||||
_field->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
_field->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
_field->setInstantReplacesEnabled(
|
||||
_controller->session().settings().replaceEmojiValue());
|
||||
_field->setMarkdownReplacesEnabled(rpl::single(true));
|
||||
_field->setEditLinkCallback(DefaultEditLinkCallback(_field));
|
||||
_field->setEditLinkCallback(
|
||||
DefaultEditLinkCallback(&_controller->session(), _field));
|
||||
|
||||
auto r = object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
|
||||
this,
|
||||
@@ -275,14 +279,13 @@ EditCaptionBox::EditCaptionBox(
|
||||
}, _wayWrap->lifetime());
|
||||
}
|
||||
|
||||
bool EditCaptionBox::emojiFilter(not_null<QEvent*> event) {
|
||||
void EditCaptionBox::emojiFilterForGeometry(not_null<QEvent*> event) {
|
||||
const auto type = event->type();
|
||||
if (type == QEvent::Move || type == QEvent::Resize) {
|
||||
// updateEmojiPanelGeometry uses not only container geometry, but
|
||||
// also container children geometries that will be updated later.
|
||||
crl::on_main(this, [=] { updateEmojiPanelGeometry(); });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditCaptionBox::updateEmojiPanelGeometry() {
|
||||
@@ -627,7 +630,8 @@ void EditCaptionBox::prepare() {
|
||||
});
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
getDelegate()->outerContainer(),
|
||||
_field);
|
||||
_field,
|
||||
&_controller->session());
|
||||
|
||||
setupEmojiPanel();
|
||||
|
||||
@@ -708,14 +712,16 @@ void EditCaptionBox::setupEmojiPanel() {
|
||||
st::emojiPanMinHeight / 2,
|
||||
st::emojiPanMinHeight);
|
||||
_emojiPanel->hide();
|
||||
_emojiPanel->getSelector()->emojiChosen(
|
||||
_emojiPanel->selector()->emojiChosen(
|
||||
) | rpl::start_with_next([=](EmojiPtr emoji) {
|
||||
Ui::InsertEmojiAtCursor(_field->textCursor(), emoji);
|
||||
}, lifetime());
|
||||
|
||||
_emojiFilter.reset(Core::InstallEventFilter(
|
||||
container,
|
||||
[=](not_null<QEvent*> event) { return emojiFilter(event); }));
|
||||
const auto filterCallback = [=](not_null<QEvent*> event) {
|
||||
emojiFilterForGeometry(event);
|
||||
return Core::EventFilter::Result::Continue;
|
||||
};
|
||||
_emojiFilter.reset(Core::InstallEventFilter(container, filterCallback));
|
||||
|
||||
_emojiToggle.create(this, st::boxAttachEmoji);
|
||||
_emojiToggle->installEventFilter(_emojiPanel);
|
||||
@@ -876,7 +882,7 @@ void EditCaptionBox::setInnerFocus() {
|
||||
void EditCaptionBox::save() {
|
||||
if (_saveRequestId) return;
|
||||
|
||||
const auto item = Auth().data().message(_msgId);
|
||||
const auto item = _controller->session().data().message(_msgId);
|
||||
if (!item) {
|
||||
_error = tr::lng_edit_deleted(tr::now);
|
||||
update();
|
||||
@@ -894,7 +900,7 @@ void EditCaptionBox::save() {
|
||||
};
|
||||
const auto prepareFlags = Ui::ItemTextOptions(
|
||||
item->history(),
|
||||
Auth().user()).flags;
|
||||
_controller->session().user()).flags;
|
||||
TextUtilities::PrepareForSending(sending, prepareFlags);
|
||||
TextUtilities::Trim(sending);
|
||||
|
||||
@@ -913,11 +919,11 @@ void EditCaptionBox::save() {
|
||||
};
|
||||
item->setText(sending);
|
||||
|
||||
Auth().api().editMedia(
|
||||
_controller->session().api().editMedia(
|
||||
std::move(_preparedList),
|
||||
(!_asFile && _photo) ? SendMediaType::Photo : SendMediaType::File,
|
||||
_field->getTextWithAppliedMarkdown(),
|
||||
ApiWrap::SendOptions(item->history()),
|
||||
Api::SendAction(item->history()),
|
||||
item->fullId().msg);
|
||||
closeBox();
|
||||
return;
|
||||
@@ -931,28 +937,32 @@ void EditCaptionBox::save() {
|
||||
MTP_string(sending.text),
|
||||
MTPInputMedia(),
|
||||
MTPReplyMarkup(),
|
||||
sentEntities),
|
||||
sentEntities,
|
||||
MTP_int(0)), // schedule_date
|
||||
rpcDone(&EditCaptionBox::saveDone),
|
||||
rpcFail(&EditCaptionBox::saveFail));
|
||||
}
|
||||
|
||||
void EditCaptionBox::saveDone(const MTPUpdates &updates) {
|
||||
_saveRequestId = 0;
|
||||
const auto controller = _controller;
|
||||
closeBox();
|
||||
Auth().api().applyUpdates(updates);
|
||||
controller->session().api().applyUpdates(updates);
|
||||
}
|
||||
|
||||
bool EditCaptionBox::saveFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
|
||||
_saveRequestId = 0;
|
||||
QString err = error.type();
|
||||
if (err == qstr("MESSAGE_ID_INVALID") || err == qstr("CHAT_ADMIN_REQUIRED") || err == qstr("MESSAGE_EDIT_TIME_EXPIRED")) {
|
||||
const auto &type = error.type();
|
||||
if (type == qstr("MESSAGE_ID_INVALID")
|
||||
|| type == qstr("CHAT_ADMIN_REQUIRED")
|
||||
|| type == qstr("MESSAGE_EDIT_TIME_EXPIRED")) {
|
||||
_error = tr::lng_edit_error(tr::now);
|
||||
} else if (err == qstr("MESSAGE_NOT_MODIFIED")) {
|
||||
} else if (type == qstr("MESSAGE_NOT_MODIFIED")) {
|
||||
closeBox();
|
||||
return true;
|
||||
} else if (err == qstr("MESSAGE_EMPTY")) {
|
||||
} else if (type == qstr("MESSAGE_EMPTY")) {
|
||||
_field->setFocus();
|
||||
_field->showError();
|
||||
} else {
|
||||
|
||||
@@ -57,7 +57,7 @@ private:
|
||||
|
||||
void setupEmojiPanel();
|
||||
void updateEmojiPanelGeometry();
|
||||
bool emojiFilter(not_null<QEvent*> event);
|
||||
void emojiFilterForGeometry(not_null<QEvent*> event);
|
||||
|
||||
void save();
|
||||
void captionResized();
|
||||
|
||||
@@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class EditColorBox::Picker : public TWidget {
|
||||
public:
|
||||
Picker(QWidget *parent, QColor color);
|
||||
Picker(QWidget *parent, Mode mode, QColor color);
|
||||
|
||||
float64 valueX() const {
|
||||
return _x;
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
base::Observable<void> &changed() {
|
||||
return _changed;
|
||||
}
|
||||
void setHSV(int hue, int saturation, int brightness);
|
||||
void setHSB(HSB hsb);
|
||||
void setRGB(int red, int green, int blue);
|
||||
|
||||
protected:
|
||||
@@ -43,8 +43,11 @@ private:
|
||||
QCursor generateCursor();
|
||||
|
||||
void preparePalette();
|
||||
void preparePaletteRGBA();
|
||||
void preparePaletteHSL();
|
||||
void updateCurrentPoint(QPoint localPosition);
|
||||
|
||||
Mode _mode;
|
||||
QColor _topleft;
|
||||
QColor _topright;
|
||||
QColor _bottomleft;
|
||||
@@ -84,7 +87,9 @@ QCursor EditColorBox::Picker::generateCursor() {
|
||||
return QCursor(QPixmap::fromImage(cursor));
|
||||
}
|
||||
|
||||
EditColorBox::Picker::Picker(QWidget *parent, QColor color) : TWidget(parent) {
|
||||
EditColorBox::Picker::Picker(QWidget *parent, Mode mode, QColor color)
|
||||
: TWidget(parent)
|
||||
, _mode(mode) {
|
||||
setCursor(generateCursor());
|
||||
|
||||
auto size = QSize(st::colorPickerSize, st::colorPickerSize);
|
||||
@@ -137,18 +142,27 @@ void EditColorBox::Picker::preparePalette() {
|
||||
if (!_paletteInvalidated) return;
|
||||
_paletteInvalidated = false;
|
||||
|
||||
auto size = _palette.width();
|
||||
if (_mode == Mode::RGBA) {
|
||||
preparePaletteRGBA();
|
||||
} else {
|
||||
preparePaletteHSL();
|
||||
}
|
||||
_palette.setDevicePixelRatio(cRetinaFactor());
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::preparePaletteRGBA() {
|
||||
const auto size = _palette.width();
|
||||
auto ints = reinterpret_cast<uint32*>(_palette.bits());
|
||||
auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
|
||||
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
|
||||
|
||||
constexpr auto Large = 1024 * 1024;
|
||||
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
|
||||
auto part = Large / size;
|
||||
const auto part = Large / size;
|
||||
|
||||
auto topleft = anim::shifted(_topleft);
|
||||
auto topright = anim::shifted(_topright);
|
||||
auto bottomleft = anim::shifted(_bottomleft);
|
||||
auto bottomright = anim::shifted(_bottomright);
|
||||
const auto topleft = anim::shifted(_topleft);
|
||||
const auto topright = anim::shifted(_topright);
|
||||
const auto bottomleft = anim::shifted(_bottomleft);
|
||||
const auto bottomright = anim::shifted(_bottomright);
|
||||
|
||||
auto y_accumulated = 0;
|
||||
for (auto y = 0; y != size; ++y, y_accumulated += part) {
|
||||
@@ -156,11 +170,11 @@ void EditColorBox::Picker::preparePalette() {
|
||||
// 0 <= y_accumulated < Large
|
||||
// 0 <= y_ratio < 256
|
||||
|
||||
auto top_ratio = 255 - y_ratio;
|
||||
auto bottom_ratio = y_ratio;
|
||||
const auto top_ratio = 256 - y_ratio;
|
||||
const auto bottom_ratio = y_ratio;
|
||||
|
||||
auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
|
||||
auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
|
||||
const auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
|
||||
const auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
|
||||
|
||||
auto x_accumulated = 0;
|
||||
for (auto x = 0; x != size; ++x, x_accumulated += part) {
|
||||
@@ -168,7 +182,7 @@ void EditColorBox::Picker::preparePalette() {
|
||||
// 0 <= x_accumulated < Large
|
||||
// 0 <= x_ratio < 256
|
||||
|
||||
auto left_ratio = 255 - x_ratio;
|
||||
auto left_ratio = 256 - x_ratio;
|
||||
auto right_ratio = x_ratio;
|
||||
|
||||
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
|
||||
@@ -177,6 +191,40 @@ void EditColorBox::Picker::preparePalette() {
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::preparePaletteHSL() {
|
||||
const auto size = _palette.width();
|
||||
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
|
||||
auto ints = reinterpret_cast<uint32*>(_palette.bits());
|
||||
|
||||
constexpr auto Large = 1024 * 1024;
|
||||
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
|
||||
const auto part = Large / size;
|
||||
|
||||
const auto lightness = _topleft.lightness();
|
||||
const auto right = anim::shifted(_bottomright);
|
||||
|
||||
for (auto y = 0; y != size; ++y) {
|
||||
const auto hue = y * 360 / size;
|
||||
const auto color = QColor::fromHsl(hue, 255, lightness).toRgb();
|
||||
const auto left = anim::shifted(anim::getPremultiplied(color));
|
||||
|
||||
auto x_accumulated = 0;
|
||||
for (auto x = 0; x != size; ++x, x_accumulated += part) {
|
||||
auto x_ratio = x_accumulated >> (LargeBit - 8); // (x_accumulated * 256) / Large;
|
||||
// 0 <= x_accumulated < Large
|
||||
// 0 <= x_ratio < 256
|
||||
|
||||
auto left_ratio = 256 - x_ratio;
|
||||
auto right_ratio = x_ratio;
|
||||
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
|
||||
}
|
||||
ints += intsAddPerLine;
|
||||
}
|
||||
|
||||
_palette = std::move(_palette).transformed(
|
||||
QTransform(0, 1, 1, 0, 0, 0));
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
|
||||
auto x = snap(localPosition.x(), 0, width()) / float64(width());
|
||||
auto y = snap(localPosition.y(), 0, height()) / float64(height());
|
||||
@@ -188,17 +236,25 @@ void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::setHSV(int hue, int saturation, int brightness) {
|
||||
_topleft = QColor(255, 255, 255);
|
||||
_topright.setHsv(qMax(0, hue), 255, 255);
|
||||
_topright = _topright.toRgb();
|
||||
_bottomleft = _bottomright = QColor(0, 0, 0);
|
||||
void EditColorBox::Picker::setHSB(HSB hsb) {
|
||||
if (_mode == Mode::RGBA) {
|
||||
_topleft = QColor(255, 255, 255);
|
||||
_topright.setHsv(qMax(0, hsb.hue), 255, 255);
|
||||
_topright = _topright.toRgb();
|
||||
_bottomleft = _bottomright = QColor(0, 0, 0);
|
||||
|
||||
_x = snap(hsb.saturation / 255., 0., 1.);
|
||||
_y = 1. - snap(hsb.brightness / 255., 0., 1.);
|
||||
} else {
|
||||
_topleft = _topright = QColor::fromHsl(0, 255, hsb.brightness);
|
||||
_bottomleft = _bottomright = QColor::fromHsl(0, 0, hsb.brightness);
|
||||
|
||||
_x = snap(hsb.hue / 360., 0., 1.);
|
||||
_y = 1. - snap(hsb.saturation / 255., 0., 1.);
|
||||
}
|
||||
|
||||
_paletteInvalidated = true;
|
||||
update();
|
||||
|
||||
_x = snap(saturation / 255., 0., 1.);
|
||||
_y = 1. - snap(brightness / 255., 0., 1.);
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::setRGB(int red, int green, int blue) {
|
||||
@@ -206,7 +262,11 @@ void EditColorBox::Picker::setRGB(int red, int green, int blue) {
|
||||
}
|
||||
|
||||
void EditColorBox::Picker::setFromColor(QColor color) {
|
||||
setHSV(color.hsvHue(), color.hsvSaturation(), color.value());
|
||||
if (_mode == Mode::RGBA) {
|
||||
setHSB({ color.hsvHue(), color.hsvSaturation(), color.value() });
|
||||
} else {
|
||||
setHSB({ color.hslHue(), color.hslSaturation(), color.lightness() });
|
||||
}
|
||||
}
|
||||
|
||||
class EditColorBox::Slider : public TWidget {
|
||||
@@ -218,6 +278,7 @@ public:
|
||||
enum class Type {
|
||||
Hue,
|
||||
Opacity,
|
||||
Lightness
|
||||
};
|
||||
Slider(QWidget *parent, Direction direction, Type type, QColor color);
|
||||
|
||||
@@ -231,10 +292,12 @@ public:
|
||||
_value = snap(value, 0., 1.);
|
||||
update();
|
||||
}
|
||||
void setHSV(int hue, int saturation, int brightness);
|
||||
void setHSB(HSB hsb);
|
||||
void setRGB(int red, int green, int blue);
|
||||
void setAlpha(int alpha);
|
||||
|
||||
void setLightnessLimits(int min, int max);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
@@ -254,10 +317,14 @@ private:
|
||||
void generatePixmap();
|
||||
void updatePixmapFromMask();
|
||||
void updateCurrentPoint(QPoint localPosition);
|
||||
[[nodiscard]] QColor applyLimits(QColor color) const;
|
||||
|
||||
Direction _direction = Direction::Horizontal;
|
||||
Type _type = Type::Hue;
|
||||
|
||||
int _lightnessMin = 0;
|
||||
int _lightnessMax = 255;
|
||||
|
||||
QColor _color;
|
||||
float64 _value = 0;
|
||||
|
||||
@@ -270,12 +337,17 @@ private:
|
||||
|
||||
};
|
||||
|
||||
EditColorBox::Slider::Slider(QWidget *parent, Direction direction, Type type, QColor color) : TWidget(parent)
|
||||
EditColorBox::Slider::Slider(
|
||||
QWidget *parent,
|
||||
Direction direction,
|
||||
Type type,
|
||||
QColor color)
|
||||
: TWidget(parent)
|
||||
, _direction(direction)
|
||||
, _type(type)
|
||||
, _color(color.red(), color.green(), color.blue())
|
||||
, _value(valueFromColor(color))
|
||||
, _transparent((_type == Type::Hue) ? QBrush() : style::transparentPlaceholderBrush()) {
|
||||
, _transparent((_type == Type::Opacity) ? style::transparentPlaceholderBrush() : QBrush()) {
|
||||
prepareMinSize();
|
||||
}
|
||||
|
||||
@@ -336,10 +408,9 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
auto part = Large / size;
|
||||
|
||||
if (_type == Type::Hue) {
|
||||
QColor color;
|
||||
for (auto x = 0; x != size; ++x) {
|
||||
color.setHsv(x * 360 / size, 255, 255);
|
||||
auto value = anim::getPremultiplied(color.toRgb());
|
||||
const auto color = QColor::fromHsv(x * 360 / size, 255, 255);
|
||||
const auto value = anim::getPremultiplied(color.toRgb());
|
||||
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
|
||||
ints[y * intsPerLine] = value;
|
||||
}
|
||||
@@ -349,7 +420,7 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
|
||||
}
|
||||
_pixmap = App::pixmapFromImageInPlace(std::move(image));
|
||||
} else {
|
||||
} else if (_type == Type::Opacity) {
|
||||
auto color = anim::shifted(QColor(255, 255, 255, 255));
|
||||
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
|
||||
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
|
||||
@@ -359,7 +430,7 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
// 0 <= x_accumulated < Large
|
||||
// 0 <= x_ratio < 256
|
||||
|
||||
*ints++ = anim::unshifted(color * x_ratio + transparent * (255 - x_ratio));
|
||||
*ints++ = anim::unshifted(color * x_ratio + transparent * (256 - x_ratio));
|
||||
}
|
||||
ints += intsPerLineAdded;
|
||||
}
|
||||
@@ -368,22 +439,45 @@ void EditColorBox::Slider::generatePixmap() {
|
||||
}
|
||||
_mask = std::move(image);
|
||||
updatePixmapFromMask();
|
||||
} else {
|
||||
const auto range = _lightnessMax - _lightnessMin;
|
||||
for (auto x = 0; x != size; ++x) {
|
||||
const auto color = QColor::fromHsl(
|
||||
_color.hslHue(),
|
||||
_color.hslSaturation(),
|
||||
_lightnessMin + x * range / size);
|
||||
const auto value = anim::getPremultiplied(color.toRgb());
|
||||
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
|
||||
ints[y * intsPerLine] = value;
|
||||
}
|
||||
++ints;
|
||||
}
|
||||
if (!isHorizontal()) {
|
||||
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
|
||||
}
|
||||
_pixmap = App::pixmapFromImageInPlace(std::move(image));
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::setHSV(int hue, int saturation, int brightness) {
|
||||
void EditColorBox::Slider::setHSB(HSB hsb) {
|
||||
if (_type == Type::Hue) {
|
||||
// hue == 360 converts to 0 if done in general way
|
||||
_value = valueFromHue(hue);
|
||||
_value = valueFromHue(hsb.hue);
|
||||
update();
|
||||
} else if (_type == Type::Opacity) {
|
||||
_color.setHsv(hsb.hue, hsb.saturation, hsb.brightness);
|
||||
colorUpdated();
|
||||
} else {
|
||||
_color.setHsv(hue, saturation, brightness);
|
||||
_color.setHsl(
|
||||
hsb.hue,
|
||||
hsb.saturation,
|
||||
std::clamp(hsb.brightness, _lightnessMin, _lightnessMax));
|
||||
colorUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::setRGB(int red, int green, int blue) {
|
||||
_color.setRgb(red, green, blue);
|
||||
_color = applyLimits(QColor(red, green, blue));
|
||||
colorUpdated();
|
||||
}
|
||||
|
||||
@@ -392,12 +486,23 @@ void EditColorBox::Slider::colorUpdated() {
|
||||
_value = valueFromColor(_color);
|
||||
} else if (!_mask.isNull()) {
|
||||
updatePixmapFromMask();
|
||||
} else {
|
||||
_value = valueFromColor(_color);
|
||||
generatePixmap();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
float64 EditColorBox::Slider::valueFromColor(QColor color) const {
|
||||
return (_type == Type::Hue) ? valueFromHue(color.hsvHue()) : color.alphaF();
|
||||
return (_type == Type::Hue)
|
||||
? valueFromHue(color.hsvHue())
|
||||
: (_type == Type::Opacity)
|
||||
? color.alphaF()
|
||||
: std::clamp(
|
||||
((color.lightness() - _lightnessMin)
|
||||
/ float64(_lightnessMax - _lightnessMin)),
|
||||
0.,
|
||||
1.);
|
||||
}
|
||||
|
||||
float64 EditColorBox::Slider::valueFromHue(int hue) const {
|
||||
@@ -411,6 +516,15 @@ void EditColorBox::Slider::setAlpha(int alpha) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::setLightnessLimits(int min, int max) {
|
||||
Expects(max > min);
|
||||
|
||||
_lightnessMin = min;
|
||||
_lightnessMax = max;
|
||||
_color = applyLimits(_color);
|
||||
colorUpdated();
|
||||
}
|
||||
|
||||
void EditColorBox::Slider::updatePixmapFromMask() {
|
||||
_pixmap = App::pixmapFromImageInPlace(style::colorizeImage(_mask, _color));
|
||||
}
|
||||
@@ -426,6 +540,19 @@ void EditColorBox::Slider::updateCurrentPoint(QPoint localPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
QColor EditColorBox::Slider::applyLimits(QColor color) const {
|
||||
if (_type != Type::Lightness) {
|
||||
return color;
|
||||
}
|
||||
|
||||
const auto lightness = color.lightness();
|
||||
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
|
||||
if (clamped == lightness) {
|
||||
return color;
|
||||
}
|
||||
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
|
||||
}
|
||||
|
||||
class EditColorBox::Field : public Ui::MaskedInputField {
|
||||
public:
|
||||
Field(QWidget *parent, const style::InputField &st, const QString &placeholder, int limit, const QString &units = QString());
|
||||
@@ -613,14 +740,18 @@ void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
|
||||
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
|
||||
}
|
||||
|
||||
EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : BoxContent()
|
||||
EditColorBox::EditColorBox(
|
||||
QWidget*,
|
||||
const QString &title,
|
||||
Mode mode,
|
||||
QColor current)
|
||||
: BoxContent()
|
||||
, _title(title)
|
||||
, _picker(this, current)
|
||||
, _hueSlider(this, Slider::Direction::Vertical, Slider::Type::Hue, current)
|
||||
, _opacitySlider(this, Slider::Direction::Horizontal, Slider::Type::Opacity, current)
|
||||
, _mode(mode)
|
||||
, _picker(this, mode, current)
|
||||
, _hueField(this, st::colorValueInput, "H", 360, QString() + QChar(176)) // degree character
|
||||
, _saturationField(this, st::colorValueInput, "S", 100, "%")
|
||||
, _brightnessField(this, st::colorValueInput, "B", 100, "%")
|
||||
, _brightnessField(this, st::colorValueInput, (mode == Mode::RGBA) ? "B" : "L", 100, "%")
|
||||
, _redField(this, st::colorValueInput, "R", 255)
|
||||
, _greenField(this, st::colorValueInput, "G", 255)
|
||||
, _blueField(this, st::colorValueInput, "B", 255)
|
||||
@@ -628,16 +759,47 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
|
||||
, _transparent(style::transparentPlaceholderBrush())
|
||||
, _current(current)
|
||||
, _new(current) {
|
||||
if (_mode == Mode::RGBA) {
|
||||
_hueSlider.create(
|
||||
this,
|
||||
Slider::Direction::Vertical,
|
||||
Slider::Type::Hue,
|
||||
current);
|
||||
_opacitySlider.create(
|
||||
this,
|
||||
Slider::Direction::Horizontal,
|
||||
Slider::Type::Opacity,
|
||||
current);
|
||||
} else if (_mode == Mode::HSL) {
|
||||
_lightnessSlider.create(
|
||||
this,
|
||||
Slider::Direction::Horizontal,
|
||||
Slider::Type::Lightness,
|
||||
current);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::setLightnessLimits(int min, int max) {
|
||||
Expects(_mode == Mode::HSL);
|
||||
|
||||
_lightnessMin = min;
|
||||
_lightnessMax = max;
|
||||
_lightnessSlider->setLightnessLimits(min, max);
|
||||
|
||||
const auto adjusted = applyLimits(_new);
|
||||
if (_new != adjusted) {
|
||||
updateFromColor(adjusted);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::prepare() {
|
||||
setTitle(rpl::single(_title));
|
||||
|
||||
const auto hsvChanged = [=] { updateFromHSVFields(); };
|
||||
const auto hsbChanged = [=] { updateFromHSBFields(); };
|
||||
const auto rgbChanged = [=] { updateFromRGBFields(); };
|
||||
connect(_hueField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_saturationField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_brightnessField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_hueField, &Ui::MaskedInputField::changed, hsbChanged);
|
||||
connect(_saturationField, &Ui::MaskedInputField::changed, hsbChanged);
|
||||
connect(_brightnessField, &Ui::MaskedInputField::changed, hsbChanged);
|
||||
connect(_redField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_greenField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_blueField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
@@ -661,8 +823,15 @@ void EditColorBox::prepare() {
|
||||
setDimensions(st::colorEditWidth, height);
|
||||
|
||||
subscribe(_picker->changed(), [=] { updateFromControls(); });
|
||||
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
|
||||
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
|
||||
if (_hueSlider) {
|
||||
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
subscribe(_lightnessSlider->changed(), [=] { updateFromControls(); });
|
||||
}
|
||||
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (_cancelCallback) {
|
||||
@@ -671,7 +840,7 @@ void EditColorBox::prepare() {
|
||||
}, lifetime());
|
||||
|
||||
updateRGBFields();
|
||||
updateHSVFields();
|
||||
updateHSBFields();
|
||||
updateResultField();
|
||||
update();
|
||||
}
|
||||
@@ -704,21 +873,21 @@ void EditColorBox::fieldSubmitted() {
|
||||
}
|
||||
|
||||
void EditColorBox::saveColor() {
|
||||
_cancelCallback = Fn<void()>();
|
||||
const auto weak = make_weak(this);
|
||||
_cancelCallback = nullptr;
|
||||
if (_saveCallback) {
|
||||
_saveCallback(_new.toRgb());
|
||||
}
|
||||
closeBox();
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::updateHSVFields() {
|
||||
auto hue = qRound((1. - _hueSlider->value()) * 360);
|
||||
auto saturation = qRound(_picker->valueX() * 255);
|
||||
auto brightness = qRound((1. - _picker->valueY()) * 255);
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
_hueField->setTextWithFocus(QString::number(hue));
|
||||
_saturationField->setTextWithFocus(QString::number(percentFromByte(saturation)));
|
||||
_brightnessField->setTextWithFocus(QString::number(percentFromByte(brightness)));
|
||||
void EditColorBox::updateHSBFields() {
|
||||
const auto hsb = hsbFromControls();
|
||||
_hueField->setTextWithFocus(QString::number(hsb.hue));
|
||||
_saturationField->setTextWithFocus(QString::number(percentFromByte(hsb.saturation)));
|
||||
_brightnessField->setTextWithFocus(QString::number(percentFromByte(hsb.brightness)));
|
||||
}
|
||||
|
||||
void EditColorBox::updateRGBFields() {
|
||||
@@ -750,14 +919,28 @@ void EditColorBox::updateResultField() {
|
||||
}
|
||||
|
||||
void EditColorBox::resizeEvent(QResizeEvent *e) {
|
||||
auto fullwidth = _picker->width() + 2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width() + st::colorSampleSize.width();
|
||||
const auto fullwidth = _picker->width()
|
||||
+ ((_mode == Mode::RGBA)
|
||||
? (2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width())
|
||||
: (2 * st::colorEditSkip))
|
||||
+ st::colorSampleSize.width();
|
||||
auto left = (width() - fullwidth) / 2;
|
||||
_picker->moveToLeft(left, st::colorEditSkip);
|
||||
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
|
||||
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
|
||||
auto fieldLeft = _hueSlider->x() + _hueSlider->width() - st::colorSliderSkip + st::colorEditSkip;
|
||||
auto fieldWidth = st::colorSampleSize.width();
|
||||
auto fieldHeight = _hueField->height();
|
||||
if (_hueSlider) {
|
||||
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
_lightnessSlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _lightnessSlider->height());
|
||||
}
|
||||
const auto fieldLeft = (_mode == Mode::RGBA)
|
||||
? (_hueSlider->x() + _hueSlider->width() + st::colorEditSkip - st::colorSliderSkip)
|
||||
: (_picker->x() + _picker->width() + st::colorEditSkip);
|
||||
const auto addWidth = (_mode == Mode::RGBA) ? 0 : st::colorEditSkip;
|
||||
const auto fieldWidth = st::colorSampleSize.width() + addWidth;
|
||||
const auto fieldHeight = _hueField->height();
|
||||
_newRect = QRect(fieldLeft, st::colorEditSkip, fieldWidth, st::colorSampleSize.height());
|
||||
_currentRect = _newRect.translated(0, st::colorSampleSize.height());
|
||||
_hueField->setGeometryToLeft(fieldLeft, _currentRect.y() + _currentRect.height() + st::colorFieldSkip, fieldWidth, fieldHeight);
|
||||
@@ -766,7 +949,13 @@ void EditColorBox::resizeEvent(QResizeEvent *e) {
|
||||
_redField->setGeometryToLeft(fieldLeft, _brightnessField->y() + _brightnessField->height() + st::colorFieldSkip, fieldWidth, fieldHeight);
|
||||
_greenField->setGeometryToLeft(fieldLeft, _redField->y() + _redField->height(), fieldWidth, fieldHeight);
|
||||
_blueField->setGeometryToLeft(fieldLeft, _greenField->y() + _greenField->height(), fieldWidth, fieldHeight);
|
||||
_result->setGeometryToLeft(fieldLeft - (st::colorEditSkip + st::colorSliderWidth), _opacitySlider->y() + _opacitySlider->height() - st::colorSliderSkip - _result->height(), fieldWidth + (st::colorEditSkip + st::colorSliderWidth), fieldHeight);
|
||||
const auto resultDelta = (_mode == Mode::RGBA)
|
||||
? (st::colorEditSkip + st::colorSliderWidth)
|
||||
: 0;
|
||||
const auto resultBottom = (_mode == Mode::RGBA)
|
||||
? (_opacitySlider->y() + _opacitySlider->height())
|
||||
: (_lightnessSlider->y() + _lightnessSlider->height());
|
||||
_result->setGeometryToLeft(fieldLeft - resultDelta, resultBottom - st::colorSliderSkip - _result->height(), fieldWidth + resultDelta, fieldHeight);
|
||||
}
|
||||
|
||||
void EditColorBox::paintEvent(QPaintEvent *e) {
|
||||
@@ -792,39 +981,74 @@ void EditColorBox::mousePressEvent(QMouseEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
EditColorBox::HSB EditColorBox::hsbFromControls() const {
|
||||
const auto hue = (_mode == Mode::RGBA)
|
||||
? qRound((1. - _hueSlider->value()) * 360)
|
||||
: qRound(_picker->valueX() * 360);
|
||||
const auto saturation = (_mode == Mode::RGBA)
|
||||
? qRound(_picker->valueX() * 255)
|
||||
: qRound((1. - _picker->valueY()) * 255);
|
||||
const auto brightness = (_mode == Mode::RGBA)
|
||||
? qRound((1. - _picker->valueY()) * 255)
|
||||
: (_lightnessMin
|
||||
+ qRound(_lightnessSlider->value()
|
||||
* (_lightnessMax - _lightnessMin)));
|
||||
return { hue, saturation, brightness };
|
||||
}
|
||||
|
||||
QColor EditColorBox::applyLimits(QColor color) const {
|
||||
if (_mode != Mode::HSL) {
|
||||
return color;
|
||||
}
|
||||
|
||||
const auto lightness = color.lightness();
|
||||
const auto clamped = std::clamp(lightness, _lightnessMin, _lightnessMax);
|
||||
if (clamped == lightness) {
|
||||
return color;
|
||||
}
|
||||
return QColor::fromHsl(color.hslHue(), color.hslSaturation(), clamped);
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromColor(QColor color) {
|
||||
_new = color;
|
||||
_new = applyLimits(color);
|
||||
updateControlsFromColor();
|
||||
updateRGBFields();
|
||||
updateHSVFields();
|
||||
updateHSBFields();
|
||||
updateResultField();
|
||||
update();
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromControls() {
|
||||
auto hue = qRound((1. - _hueSlider->value()) * 360);
|
||||
auto saturation = qRound(_picker->valueX() * 255);
|
||||
auto brightness = qRound((1. - _picker->valueY()) * 255);
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
setHSV(hue, saturation, brightness, alpha);
|
||||
updateHSVFields();
|
||||
updateControlsFromHSV(hue, saturation, brightness);
|
||||
const auto hsb = hsbFromControls();
|
||||
const auto alpha = _opacitySlider
|
||||
? qRound(_opacitySlider->value() * 255)
|
||||
: 255;
|
||||
setHSB(hsb, alpha);
|
||||
updateHSBFields();
|
||||
updateControlsFromHSB(hsb);
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromHSVFields() {
|
||||
auto hue = _hueField->value();
|
||||
auto saturation = percentToByte(_saturationField->value());
|
||||
auto brightness = percentToByte(_brightnessField->value());
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
setHSV(hue, saturation, brightness, alpha);
|
||||
updateControlsFromHSV(hue, saturation, brightness);
|
||||
void EditColorBox::updateFromHSBFields() {
|
||||
const auto hue = _hueField->value();
|
||||
const auto saturation = percentToByte(_saturationField->value());
|
||||
const auto brightness = std::clamp(
|
||||
percentToByte(_brightnessField->value()),
|
||||
_lightnessMin,
|
||||
_lightnessMax);
|
||||
const auto alpha = _opacitySlider
|
||||
? qRound(_opacitySlider->value() * 255)
|
||||
: 255;
|
||||
setHSB({ hue, saturation, brightness }, alpha);
|
||||
updateControlsFromHSB({ hue, saturation, brightness });
|
||||
}
|
||||
|
||||
void EditColorBox::updateFromRGBFields() {
|
||||
auto red = _redField->value();
|
||||
auto blue = _blueField->value();
|
||||
auto green = _greenField->value();
|
||||
auto alpha = qRound(_opacitySlider->value() * 255);
|
||||
const auto red = _redField->value();
|
||||
const auto blue = _blueField->value();
|
||||
const auto green = _greenField->value();
|
||||
const auto alpha = _opacitySlider
|
||||
? qRound(_opacitySlider->value() * 255)
|
||||
: 255;
|
||||
setRGB(red, green, blue, alpha);
|
||||
updateResultField();
|
||||
}
|
||||
@@ -855,10 +1079,17 @@ void EditColorBox::updateFromResultField() {
|
||||
updateRGBFields();
|
||||
}
|
||||
|
||||
void EditColorBox::updateControlsFromHSV(int hue, int saturation, int brightness) {
|
||||
_picker->setHSV(hue, saturation, brightness);
|
||||
_hueSlider->setHSV(hue, saturation, brightness);
|
||||
_opacitySlider->setHSV(hue, saturation, brightness);
|
||||
void EditColorBox::updateControlsFromHSB(HSB hsb) {
|
||||
_picker->setHSB(hsb);
|
||||
if (_hueSlider) {
|
||||
_hueSlider->setHSB(hsb);
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
_opacitySlider->setHSB(hsb);
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
_lightnessSlider->setHSB(hsb);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::updateControlsFromColor() {
|
||||
@@ -867,21 +1098,32 @@ void EditColorBox::updateControlsFromColor() {
|
||||
auto blue = _new.blue();
|
||||
auto alpha = _new.alpha();
|
||||
_picker->setRGB(red, green, blue);
|
||||
_hueSlider->setRGB(red, green, blue);
|
||||
_opacitySlider->setRGB(red, green, blue);
|
||||
_opacitySlider->setAlpha(alpha);
|
||||
if (_hueSlider) {
|
||||
_hueSlider->setRGB(red, green, blue);
|
||||
}
|
||||
if (_opacitySlider) {
|
||||
_opacitySlider->setRGB(red, green, blue);
|
||||
_opacitySlider->setAlpha(alpha);
|
||||
}
|
||||
if (_lightnessSlider) {
|
||||
_lightnessSlider->setRGB(red, green, blue);
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
|
||||
_new.setHsv(hue, saturation, value, alpha);
|
||||
void EditColorBox::setHSB(HSB hsb, int alpha) {
|
||||
if (_mode == Mode::RGBA) {
|
||||
_new.setHsv(hsb.hue, hsb.saturation, hsb.brightness, alpha);
|
||||
} else {
|
||||
_new.setHsl(hsb.hue, hsb.saturation, hsb.brightness, alpha);
|
||||
}
|
||||
updateRGBFields();
|
||||
updateResultField();
|
||||
update();
|
||||
}
|
||||
|
||||
void EditColorBox::setRGB(int red, int green, int blue, int alpha) {
|
||||
_new.setRgb(red, green, blue, alpha);
|
||||
_new = applyLimits(QColor(red, green, blue, alpha));
|
||||
updateControlsFromColor();
|
||||
updateHSVFields();
|
||||
updateHSBFields();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
class EditColorBox : public BoxContent {
|
||||
public:
|
||||
EditColorBox(QWidget*, const QString &title, QColor current = QColor(255, 255, 255));
|
||||
enum class Mode {
|
||||
RGBA,
|
||||
HSL,
|
||||
};
|
||||
EditColorBox(QWidget*, const QString &title, Mode mode, QColor current);
|
||||
|
||||
void setLightnessLimits(int min, int max);
|
||||
|
||||
void setSaveCallback(Fn<void(QColor)> callback) {
|
||||
_saveCallback = std::move(callback);
|
||||
@@ -36,21 +42,28 @@ protected:
|
||||
void setInnerFocus() override;
|
||||
|
||||
private:
|
||||
struct HSB { // HSV or HSL depending on Mode.
|
||||
int hue = 0;
|
||||
int saturation = 0;
|
||||
int brightness = 0;
|
||||
};
|
||||
void saveColor();
|
||||
void fieldSubmitted();
|
||||
|
||||
[[nodiscard]] HSB hsbFromControls() const;
|
||||
void updateFromColor(QColor color);
|
||||
void updateControlsFromColor();
|
||||
void updateControlsFromHSV(int hue, int saturation, int brightness);
|
||||
void updateHSVFields();
|
||||
void updateControlsFromHSB(HSB hsb);
|
||||
void updateHSBFields();
|
||||
void updateRGBFields();
|
||||
void updateResultField();
|
||||
void updateFromControls();
|
||||
void updateFromHSVFields();
|
||||
void updateFromHSBFields();
|
||||
void updateFromRGBFields();
|
||||
void updateFromResultField();
|
||||
void setHSV(int hue, int saturation, int brightness, int alpha);
|
||||
void setHSB(HSB hsb, int alpha);
|
||||
void setRGB(int red, int green, int blue, int alpha);
|
||||
[[nodiscard]] QColor applyLimits(QColor color) const;
|
||||
|
||||
int percentFromByte(int byte) {
|
||||
return snap(qRound(byte * 100 / 255.), 0, 100);
|
||||
@@ -65,10 +78,12 @@ private:
|
||||
class ResultField;
|
||||
|
||||
QString _title;
|
||||
Mode _mode = Mode();
|
||||
|
||||
object_ptr<Picker> _picker;
|
||||
object_ptr<Slider> _hueSlider;
|
||||
object_ptr<Slider> _opacitySlider;
|
||||
object_ptr<Slider> _hueSlider = { nullptr };
|
||||
object_ptr<Slider> _opacitySlider = { nullptr };
|
||||
object_ptr<Slider> _lightnessSlider = { nullptr };
|
||||
|
||||
object_ptr<Field> _hueField;
|
||||
object_ptr<Field> _saturationField;
|
||||
@@ -85,6 +100,9 @@ private:
|
||||
QRect _currentRect;
|
||||
QRect _newRect;
|
||||
|
||||
int _lightnessMin = 0;
|
||||
int _lightnessMax = 255;
|
||||
|
||||
Fn<void(QColor)> _saveCallback;
|
||||
Fn<void()> _cancelCallback;
|
||||
|
||||
|
||||
@@ -21,10 +21,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/binary_guard.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_settings.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -33,8 +34,11 @@ namespace {
|
||||
class PrivacyExceptionsBoxController : public ChatsListBoxController {
|
||||
public:
|
||||
PrivacyExceptionsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
rpl::producer<QString> title,
|
||||
const std::vector<not_null<PeerData*>> &selected);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
std::vector<not_null<PeerData*>> getResult() const;
|
||||
@@ -44,18 +48,26 @@ protected:
|
||||
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
not_null<Window::SessionNavigation*> _navigation;
|
||||
rpl::producer<QString> _title;
|
||||
std::vector<not_null<PeerData*>> _selected;
|
||||
|
||||
};
|
||||
|
||||
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
rpl::producer<QString> title,
|
||||
const std::vector<not_null<PeerData*>> &selected)
|
||||
: _title(std::move(title))
|
||||
: ChatsListBoxController(navigation)
|
||||
, _navigation(navigation)
|
||||
, _title(std::move(title))
|
||||
, _selected(selected) {
|
||||
}
|
||||
|
||||
Main::Session &PrivacyExceptionsBoxController::session() const {
|
||||
return _navigation->session();
|
||||
}
|
||||
|
||||
void PrivacyExceptionsBoxController::prepareViewHook() {
|
||||
delegate()->peerListSetTitle(std::move(_title));
|
||||
delegate()->peerListAddSelectedRows(_selected);
|
||||
@@ -113,9 +125,11 @@ QString EditPrivacyController::optionLabel(Option option) {
|
||||
|
||||
EditPrivacyBox::EditPrivacyBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> window,
|
||||
std::unique_ptr<EditPrivacyController> controller,
|
||||
const Value &value)
|
||||
: _controller(std::move(controller))
|
||||
: _window(window)
|
||||
, _controller(std::move(controller))
|
||||
, _value(value) {
|
||||
}
|
||||
|
||||
@@ -129,6 +143,7 @@ void EditPrivacyBox::editExceptions(
|
||||
Exception exception,
|
||||
Fn<void()> done) {
|
||||
auto controller = std::make_unique<PrivacyExceptionsBoxController>(
|
||||
_window,
|
||||
_controller->exceptionBoxTitle(exception),
|
||||
exceptions(exception));
|
||||
auto initBox = [=, controller = controller.get()](
|
||||
@@ -235,8 +250,9 @@ bool EditPrivacyBox::showExceptionLink(Exception exception) const {
|
||||
Unexpected("Invalid exception value.");
|
||||
}
|
||||
|
||||
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::addOption(
|
||||
Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::AddOption(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<EditPrivacyController*> controller,
|
||||
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
|
||||
Option option) {
|
||||
return container->add(
|
||||
@@ -244,7 +260,7 @@ Ui::Radioenum<EditPrivacyBox::Option> *EditPrivacyBox::addOption(
|
||||
container,
|
||||
group,
|
||||
option,
|
||||
_controller->optionLabel(option),
|
||||
controller->optionLabel(option),
|
||||
st::settingsSendType),
|
||||
st::settingsSendTypePadding);
|
||||
}
|
||||
@@ -291,7 +307,7 @@ void EditPrivacyBox::setupContent() {
|
||||
|
||||
const auto addOptionRow = [&](Option option) {
|
||||
return (_controller->hasOption(option) || (_value.option == option))
|
||||
? addOption(content, group, option)
|
||||
? AddOption(content, _controller.get(), group, option)
|
||||
: nullptr;
|
||||
};
|
||||
const auto addExceptionLink = [=](Exception exception) {
|
||||
@@ -330,7 +346,7 @@ void EditPrivacyBox::setupContent() {
|
||||
|
||||
auto above = _controller->setupAboveWidget(
|
||||
content,
|
||||
std::move(optionValue));
|
||||
rpl::duplicate(optionValue));
|
||||
if (above) {
|
||||
content->add(std::move(above));
|
||||
}
|
||||
@@ -342,6 +358,14 @@ void EditPrivacyBox::setupContent() {
|
||||
addLabel(content, _controller->warning());
|
||||
AddSkip(content);
|
||||
|
||||
auto middle = _controller->setupMiddleWidget(
|
||||
_window,
|
||||
content,
|
||||
std::move(optionValue));
|
||||
if (middle) {
|
||||
content->add(std::move(middle));
|
||||
}
|
||||
|
||||
AddDivider(content);
|
||||
AddSkip(content);
|
||||
AddSubsectionTitle(content, tr::lng_edit_privacy_exceptions());
|
||||
@@ -350,7 +374,7 @@ void EditPrivacyBox::setupContent() {
|
||||
addLabel(content, _controller->exceptionsDescription());
|
||||
AddSkip(content);
|
||||
|
||||
if (auto below = _controller->setupBelowWidget(content)) {
|
||||
if (auto below = _controller->setupBelowWidget(_window, content)) {
|
||||
content->add(std::move(below));
|
||||
}
|
||||
|
||||
@@ -358,7 +382,8 @@ void EditPrivacyBox::setupContent() {
|
||||
const auto someAreDisallowed = (_value.option != Option::Everyone)
|
||||
|| !_value.never.empty();
|
||||
_controller->confirmSave(someAreDisallowed, crl::guard(this, [=] {
|
||||
Auth().api().savePrivacy(
|
||||
_controller->saveAdditional();
|
||||
_window->session().api().savePrivacy(
|
||||
_controller->apiKey(),
|
||||
collectResult());
|
||||
closeBox();
|
||||
|
||||
@@ -58,7 +58,14 @@ public:
|
||||
rpl::producer<Option> option) {
|
||||
return { nullptr };
|
||||
}
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupMiddleWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<QWidget*> parent,
|
||||
rpl::producer<Option> option) {
|
||||
return { nullptr };
|
||||
}
|
||||
[[nodiscard]] virtual object_ptr<Ui::RpWidget> setupBelowWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<QWidget*> parent) {
|
||||
return { nullptr };
|
||||
}
|
||||
@@ -68,6 +75,8 @@ public:
|
||||
FnMut<void()> saveCallback) {
|
||||
saveCallback();
|
||||
}
|
||||
virtual void saveAdditional() {
|
||||
}
|
||||
|
||||
virtual ~EditPrivacyController() = default;
|
||||
|
||||
@@ -95,9 +104,16 @@ public:
|
||||
|
||||
EditPrivacyBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionController*> window,
|
||||
std::unique_ptr<EditPrivacyController> controller,
|
||||
const Value &value);
|
||||
|
||||
static Ui::Radioenum<Option> *AddOption(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
not_null<EditPrivacyController*> controller,
|
||||
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
|
||||
Option option);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
@@ -106,10 +122,6 @@ private:
|
||||
void setupContent();
|
||||
QVector<MTPInputPrivacyRule> collectResult();
|
||||
|
||||
Ui::Radioenum<Option> *addOption(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
const std::shared_ptr<Ui::RadioenumGroup<Option>> &group,
|
||||
Option option);
|
||||
Ui::FlatLabel *addLabel(
|
||||
not_null<Ui::VerticalLayout*> container,
|
||||
rpl::producer<QString> text);
|
||||
@@ -117,6 +129,7 @@ private:
|
||||
void editExceptions(Exception exception, Fn<void()> done);
|
||||
std::vector<not_null<PeerData*>> &exceptions(Exception exception);
|
||||
|
||||
const not_null<Window::SessionController*> _window;
|
||||
std::unique_ptr<EditPrivacyController> _controller;
|
||||
Value _value;
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ template <std::size_t... I>
|
||||
inline void GenericBox::Initer<InitMethod, InitArgs...>::call(
|
||||
not_null<GenericBox*> box,
|
||||
std::index_sequence<I...>) {
|
||||
std::invoke(method, box, std::get<I>(args)...);
|
||||
std::invoke(method, box, std::get<I>(std::move(args))...);
|
||||
}
|
||||
|
||||
template <typename InitMethod, typename ...InitArgs>
|
||||
|
||||
@@ -32,6 +32,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_passport.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
using Language = Lang::Language;
|
||||
@@ -408,7 +411,7 @@ bool Rows::hasMenu(not_null<const Row*> row) const {
|
||||
|
||||
void Rows::share(not_null<const Row*> row) const {
|
||||
const auto link = qsl("https://t.me/setlanguage/") + row->data.id;
|
||||
QApplication::clipboard()->setText(link);
|
||||
QGuiApplication::clipboard()->setText(link);
|
||||
Ui::Toast::Show(tr::lng_username_copied(tr::now));
|
||||
}
|
||||
|
||||
@@ -1155,13 +1158,13 @@ not_null<Ui::MultiSelect*> LanguageBox::createMultiSelect() {
|
||||
base::binary_guard LanguageBox::Show() {
|
||||
auto result = base::binary_guard();
|
||||
|
||||
const auto manager = Core::App().langCloudManager();
|
||||
if (manager->languageList().empty()) {
|
||||
auto &manager = Lang::CurrentCloudManager();
|
||||
if (manager.languageList().empty()) {
|
||||
auto guard = std::make_shared<base::binary_guard>(
|
||||
result.make_guard());
|
||||
auto alive = std::make_shared<std::unique_ptr<base::Subscription>>(
|
||||
std::make_unique<base::Subscription>());
|
||||
**alive = manager->languageListChanged().add_subscription([=] {
|
||||
**alive = manager.languageListChanged().add_subscription([=] {
|
||||
const auto show = guard->alive();
|
||||
*alive = nullptr;
|
||||
if (show) {
|
||||
@@ -1171,7 +1174,7 @@ base::binary_guard LanguageBox::Show() {
|
||||
} else {
|
||||
Ui::show(Box<LanguageBox>());
|
||||
}
|
||||
manager->requestLanguageList();
|
||||
manager.requestLanguageList();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "layout.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
@@ -265,11 +265,11 @@ QString LocalStorageBox::Row::sizeText(const Database::TaggedSummary &data) cons
|
||||
|
||||
LocalStorageBox::LocalStorageBox(
|
||||
QWidget*,
|
||||
not_null<Database*> db,
|
||||
not_null<Database*> dbBig,
|
||||
not_null<Main::Session*> session,
|
||||
CreateTag)
|
||||
: _db(db)
|
||||
, _dbBig(dbBig) {
|
||||
: _session(session)
|
||||
, _db(&session->data().cache())
|
||||
, _dbBig(&session->data().cacheBigFile()) {
|
||||
const auto &settings = Local::cacheSettings();
|
||||
const auto &settingsBig = Local::cacheBigFileSettings();
|
||||
_totalSizeLimit = settings.totalSizeLimit + settingsBig.totalSizeLimit;
|
||||
@@ -277,15 +277,13 @@ LocalStorageBox::LocalStorageBox(
|
||||
_timeLimit = settings.totalTimeLimit;
|
||||
}
|
||||
|
||||
void LocalStorageBox::Show(
|
||||
not_null<Database*> db,
|
||||
not_null<Database*> dbBig) {
|
||||
void LocalStorageBox::Show(not_null<::Main::Session*> session) {
|
||||
auto shared = std::make_shared<object_ptr<LocalStorageBox>>(
|
||||
Box<LocalStorageBox>(db, dbBig, CreateTag()));
|
||||
Box<LocalStorageBox>(session, CreateTag()));
|
||||
const auto weak = shared->data();
|
||||
rpl::combine(
|
||||
db->statsOnMain(),
|
||||
dbBig->statsOnMain()
|
||||
session->data().cache().statsOnMain(),
|
||||
session->data().cacheBigFile().statsOnMain()
|
||||
) | rpl::start_with_next([=](
|
||||
Database::Stats &&stats,
|
||||
Database::Stats &&statsBig) {
|
||||
@@ -592,7 +590,7 @@ void LocalStorageBox::save() {
|
||||
updateBig.totalSizeLimit = _mediaSizeLimit;
|
||||
updateBig.totalTimeLimit = _timeLimit;
|
||||
Local::updateCacheSettings(update, updateBig);
|
||||
Auth().data().cache().updateSettings(update);
|
||||
_session->data().cache().updateSettings(update);
|
||||
closeBox();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "storage/cache/storage_cache_database.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Storage {
|
||||
namespace Cache {
|
||||
class Database;
|
||||
@@ -33,11 +37,10 @@ public:
|
||||
|
||||
LocalStorageBox(
|
||||
QWidget*,
|
||||
not_null<Database*> db,
|
||||
not_null<Database*> dbBig,
|
||||
not_null<Main::Session*> session,
|
||||
CreateTag);
|
||||
|
||||
static void Show(not_null<Database*> db, not_null<Database*> dbBig);
|
||||
static void Show(not_null<Main::Session*> session);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -80,8 +83,10 @@ private:
|
||||
Value currentValue,
|
||||
Callback &&callback);
|
||||
|
||||
not_null<Storage::Cache::Database*> _db;
|
||||
not_null<Storage::Cache::Database*> _dbBig;
|
||||
const not_null<Main::Session*> _session;
|
||||
const not_null<Storage::Cache::Database*> _db;
|
||||
const not_null<Storage::Cache::Database*> _dbBig;
|
||||
|
||||
Database::Stats _stats;
|
||||
Database::Stats _statsBig;
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
|
||||
#include "boxes/mute_settings_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -73,7 +74,7 @@ void MuteSettingsBox::prepare() {
|
||||
|
||||
_save = [=] {
|
||||
const auto muteForSeconds = group->value() * 3600;
|
||||
Auth().data().updateNotifySettings(
|
||||
_peer->session().data().updateNotifySettings(
|
||||
_peer,
|
||||
muteForSeconds);
|
||||
closeBox();
|
||||
|
||||
@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/confirm_phone_box.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
@@ -40,8 +40,12 @@ PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
|
||||
return result;
|
||||
}
|
||||
|
||||
PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
|
||||
: _turningOff(turningOff)
|
||||
PasscodeBox::PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
bool turningOff)
|
||||
: _session(session)
|
||||
, _turningOff(turningOff)
|
||||
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
||||
, _oldPasscode(this, st::defaultInputField, tr::lng_passcode_enter_old())
|
||||
, _newPasscode(this, st::defaultInputField, Global::LocalPasscode() ? tr::lng_passcode_enter_new() : tr::lng_passcode_enter_first())
|
||||
@@ -51,8 +55,12 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
|
||||
, _recover(this, tr::lng_signin_recover(tr::now)) {
|
||||
}
|
||||
|
||||
PasscodeBox::PasscodeBox(QWidget*, const CloudFields &fields)
|
||||
: _turningOff(fields.turningOff)
|
||||
PasscodeBox::PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const CloudFields &fields)
|
||||
: _session(session)
|
||||
, _turningOff(fields.turningOff)
|
||||
, _cloudPwd(true)
|
||||
, _cloudFields(fields)
|
||||
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
||||
@@ -506,7 +514,7 @@ void PasscodeBox::save(bool force) {
|
||||
const auto weak = make_weak(this);
|
||||
cSetPasscodeBadTries(0);
|
||||
Local::setPasscode(pwd.toUtf8());
|
||||
Auth().localPasscodeChanged();
|
||||
_session->localPasscodeChanged();
|
||||
if (weak) {
|
||||
closeBox();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/sender.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class InputField;
|
||||
class PasswordInput;
|
||||
@@ -23,7 +27,7 @@ struct CloudPasswordState;
|
||||
|
||||
class PasscodeBox : public BoxContent, private MTP::Sender {
|
||||
public:
|
||||
PasscodeBox(QWidget*, bool turningOff);
|
||||
PasscodeBox(QWidget*, not_null<Main::Session*> session, bool turningOff);
|
||||
|
||||
struct CloudFields {
|
||||
static CloudFields From(const Core::CloudPasswordState ¤t);
|
||||
@@ -42,7 +46,10 @@ public:
|
||||
std::optional<QString> customDescription;
|
||||
rpl::producer<QString> customSubmitButton;
|
||||
};
|
||||
PasscodeBox(QWidget*, const CloudFields &fields);
|
||||
PasscodeBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
const CloudFields &fields);
|
||||
|
||||
rpl::producer<QByteArray> newPasswordSet() const;
|
||||
rpl::producer<> passwordReloadNeeded() const;
|
||||
@@ -122,6 +129,8 @@ private:
|
||||
void passwordChecked();
|
||||
void serverError();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
QString _pattern;
|
||||
|
||||
QPointer<BoxContent> _replacedBy;
|
||||
|
||||
@@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "ui/widgets/multi_select.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_session.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
|
||||
auto PaintUserpicCallback(
|
||||
@@ -57,7 +58,7 @@ void PeerListBox::createMultiSelect() {
|
||||
Expects(_select == nullptr);
|
||||
|
||||
auto entity = object_ptr<Ui::MultiSelect>(
|
||||
this,
|
||||
this,
|
||||
st::contactsMultiSelect,
|
||||
tr::lng_participant_filter());
|
||||
_select.create(this, std::move(entity));
|
||||
@@ -65,11 +66,15 @@ void PeerListBox::createMultiSelect() {
|
||||
) | rpl::start_with_next(
|
||||
[this] { updateScrollSkips(); },
|
||||
lifetime());
|
||||
_select->entity()->setSubmittedCallback([this](Qt::KeyboardModifiers) { content()->submitted(); });
|
||||
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
|
||||
_select->entity()->setItemRemovedCallback([this](uint64 itemId) {
|
||||
if (auto peer = Auth().data().peerLoaded(itemId)) {
|
||||
if (auto row = peerListFindRow(peer->id)) {
|
||||
_select->entity()->setSubmittedCallback([=](Qt::KeyboardModifiers) {
|
||||
content()->submitted();
|
||||
});
|
||||
_select->entity()->setQueryChangedCallback([=](const QString &query) {
|
||||
searchQueryChanged(query);
|
||||
});
|
||||
_select->entity()->setItemRemovedCallback([=](uint64 itemId) {
|
||||
if (const auto peer = _controller->session().data().peerLoaded(itemId)) {
|
||||
if (const auto row = peerListFindRow(peer->id)) {
|
||||
content()->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
|
||||
update();
|
||||
}
|
||||
@@ -335,7 +340,7 @@ auto PeerListBox::peerListCollectSelectedRows()
|
||||
if (!items.empty()) {
|
||||
result.reserve(items.size());
|
||||
for (const auto itemId : items) {
|
||||
result.push_back(Auth().data().peer(itemId));
|
||||
result.push_back(_controller->session().data().peer(itemId));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -377,7 +382,7 @@ void PeerListRow::refreshStatus() {
|
||||
if (_isSavedMessagesChat) {
|
||||
setStatusText(tr::lng_saved_forward_here(tr::now));
|
||||
} else {
|
||||
auto time = unixtime();
|
||||
auto time = base::unixtime::now();
|
||||
setStatusText(Data::OnlineText(user, time));
|
||||
if (Data::OnlineTextActive(user, time)) {
|
||||
_statusType = StatusType::Online;
|
||||
@@ -573,7 +578,9 @@ PeerListContent::PeerListContent(
|
||||
, _st(st)
|
||||
, _controller(controller)
|
||||
, _rowHeight(_st.item.height) {
|
||||
subscribe(Auth().downloaderTaskFinished(), [this] { update(); });
|
||||
subscribe(_controller->session().downloaderTaskFinished(), [=] {
|
||||
update();
|
||||
});
|
||||
|
||||
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||
auto changes = UpdateFlag::NameChanged | UpdateFlag::PhotoChanged;
|
||||
@@ -1278,7 +1285,7 @@ void PeerListContent::loadProfilePhotos() {
|
||||
|
||||
auto yFrom = _visibleTop;
|
||||
auto yTo = _visibleBottom + (_visibleBottom - _visibleTop) * PreloadHeightsCount;
|
||||
Auth().downloader().clearPriorities();
|
||||
_controller->session().downloader().clearPriorities();
|
||||
|
||||
if (yTo < 0) return;
|
||||
if (yFrom < 0) yFrom = 0;
|
||||
|
||||
@@ -19,6 +19,10 @@ struct PeerList;
|
||||
struct PeerListItem;
|
||||
} // namespace style
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class RippleAnimation;
|
||||
class RoundImageCheckbox;
|
||||
@@ -334,6 +338,7 @@ public:
|
||||
|
||||
virtual void prepare() = 0;
|
||||
virtual void rowClicked(not_null<PeerListRow*> row) = 0;
|
||||
virtual Main::Session &session() const = 0;
|
||||
virtual void rowActionClicked(not_null<PeerListRow*> row) {
|
||||
}
|
||||
virtual void loadMoreRows() {
|
||||
|
||||
@@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "observer_peer.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
@@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "history/history.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_profile.h"
|
||||
|
||||
@@ -29,24 +30,28 @@ namespace {
|
||||
void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
|
||||
const auto history = chat->owner().historyLoaded(chat);
|
||||
const auto randomId = rand_value<uint64>();
|
||||
const auto requestId = MTP::send(
|
||||
MTPmessages_SendMedia(
|
||||
MTP_flags(0),
|
||||
chat->input,
|
||||
MTP_int(0),
|
||||
MTP_inputMediaGame(
|
||||
MTP_inputGameShortName(
|
||||
bot->inputUser,
|
||||
MTP_string(bot->botInfo->shareGameShortName))),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>()),
|
||||
App::main()->rpcDone(&MainWidget::sentUpdatesReceived),
|
||||
App::main()->rpcFail(&MainWidget::sendMessageFail),
|
||||
0,
|
||||
0,
|
||||
history ? history->sendRequestId : 0);
|
||||
const auto api = &chat->session().api();
|
||||
const auto requestId = api->request(MTPmessages_SendMedia(
|
||||
MTP_flags(0),
|
||||
chat->input,
|
||||
MTP_int(0),
|
||||
MTP_inputMediaGame(
|
||||
MTP_inputGameShortName(
|
||||
bot->inputUser,
|
||||
MTP_string(bot->botInfo->shareGameShortName))),
|
||||
MTP_string(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(0) // schedule_date
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->applyUpdates(result, randomId);
|
||||
}).fail([=](const RPCError &error) {
|
||||
api->sendMessageFail(error, chat);
|
||||
}).afterRequest(
|
||||
history ? history->sendRequestId : 0
|
||||
).send();
|
||||
|
||||
if (history) {
|
||||
history->sendRequestId = requestId;
|
||||
}
|
||||
@@ -55,10 +60,10 @@ void ShareBotGame(not_null<UserData*> bot, not_null<PeerData*> chat) {
|
||||
}
|
||||
|
||||
void AddBotToGroup(not_null<UserData*> bot, not_null<PeerData*> chat) {
|
||||
if (bot->botInfo && !bot->botInfo->startGroupToken.isEmpty()) {
|
||||
Auth().api().sendBotStart(bot, chat);
|
||||
if (bot->isBot() && !bot->botInfo->startGroupToken.isEmpty()) {
|
||||
chat->session().api().sendBotStart(bot, chat);
|
||||
} else {
|
||||
Auth().api().addChatParticipants(chat, { 1, bot });
|
||||
chat->session().api().addChatParticipants(chat, { 1, bot });
|
||||
}
|
||||
Ui::hideLayer();
|
||||
Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
|
||||
@@ -133,7 +138,9 @@ void PeerListRowWithLink::paintAction(
|
||||
p.drawTextLeft(x, y, outerWidth, _action, _actionWidth);
|
||||
}
|
||||
|
||||
PeerListGlobalSearchController::PeerListGlobalSearchController() {
|
||||
PeerListGlobalSearchController::PeerListGlobalSearchController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: _navigation(navigation) {
|
||||
_timer.setCallback([this] { searchOnServer(); });
|
||||
}
|
||||
|
||||
@@ -182,8 +189,8 @@ void PeerListGlobalSearchController::searchDone(
|
||||
auto &contacts = result.c_contacts_found();
|
||||
auto query = _query;
|
||||
if (requestId) {
|
||||
Auth().data().processUsers(contacts.vusers());
|
||||
Auth().data().processChats(contacts.vchats());
|
||||
_navigation->session().data().processUsers(contacts.vusers());
|
||||
_navigation->session().data().processChats(contacts.vchats());
|
||||
auto it = _queries.find(requestId);
|
||||
if (it != _queries.cend()) {
|
||||
query = it->second;
|
||||
@@ -193,7 +200,9 @@ void PeerListGlobalSearchController::searchDone(
|
||||
}
|
||||
const auto feedList = [&](const MTPVector<MTPPeer> &list) {
|
||||
for (const auto &mtpPeer : list.v) {
|
||||
if (const auto peer = Auth().data().peerLoaded(peerFromMTP(mtpPeer))) {
|
||||
const auto peer = _navigation->session().data().peerLoaded(
|
||||
peerFromMTP(mtpPeer));
|
||||
if (peer) {
|
||||
delegate()->peerListSearchAddRow(peer);
|
||||
}
|
||||
}
|
||||
@@ -215,6 +224,12 @@ ChatsListBoxController::Row::Row(not_null<History*> history)
|
||||
, _history(history) {
|
||||
}
|
||||
|
||||
ChatsListBoxController::ChatsListBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: ChatsListBoxController(
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation)) {
|
||||
}
|
||||
|
||||
ChatsListBoxController::ChatsListBoxController(
|
||||
std::unique_ptr<PeerListSearchController> searchController)
|
||||
: PeerListController(std::move(searchController)) {
|
||||
@@ -226,8 +241,8 @@ void ChatsListBoxController::prepare() {
|
||||
|
||||
prepareViewHook();
|
||||
|
||||
if (!Auth().data().chatsListLoaded()) {
|
||||
Auth().data().chatsListLoadedEvents(
|
||||
if (!session().data().chatsListLoaded()) {
|
||||
session().data().chatsListLoadedEvents(
|
||||
) | rpl::filter([=](Data::Folder *folder) {
|
||||
return !folder;
|
||||
}) | rpl::start_with_next([=] {
|
||||
@@ -235,12 +250,12 @@ void ChatsListBoxController::prepare() {
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
Auth().data().chatsListChanges(
|
||||
session().data().chatsListChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
rebuildRows();
|
||||
}, lifetime());
|
||||
|
||||
Auth().data().contactsLoaded().value(
|
||||
session().data().contactsLoaded().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
rebuildRows();
|
||||
}, lifetime());
|
||||
@@ -261,16 +276,16 @@ void ChatsListBoxController::rebuildRows() {
|
||||
};
|
||||
auto added = 0;
|
||||
if (respectSavedMessagesChat()) {
|
||||
if (appendRow(Auth().data().history(Auth().user()))) {
|
||||
if (appendRow(session().data().history(session().user()))) {
|
||||
++added;
|
||||
}
|
||||
}
|
||||
added += appendList(Auth().data().chatsList()->indexed());
|
||||
added += appendList(session().data().chatsList()->indexed());
|
||||
const auto id = Data::Folder::kId;
|
||||
if (const auto folder = Auth().data().folderLoaded(id)) {
|
||||
if (const auto folder = session().data().folderLoaded(id)) {
|
||||
added += appendList(folder->chatsList()->indexed());
|
||||
}
|
||||
added += appendList(Auth().data().contactsNoChatsList());
|
||||
added += appendList(session().data().contactsNoChatsList());
|
||||
if (!wasEmpty && added > 0) {
|
||||
// Place dialogs list before contactsNoDialogs list.
|
||||
delegate()->peerListPartitionRows([](const PeerListRow &a) {
|
||||
@@ -291,8 +306,8 @@ void ChatsListBoxController::checkForEmptyRows() {
|
||||
if (delegate()->peerListFullRowsCount()) {
|
||||
setDescriptionText(QString());
|
||||
} else {
|
||||
const auto loaded = Auth().data().contactsLoaded().current()
|
||||
&& Auth().data().chatsListLoaded();
|
||||
const auto loaded = session().data().contactsLoaded().current()
|
||||
&& session().data().chatsListLoaded();
|
||||
setDescriptionText(loaded ? emptyBoxText() : tr::lng_contacts_loading(tr::now));
|
||||
}
|
||||
}
|
||||
@@ -318,8 +333,21 @@ bool ChatsListBoxController::appendRow(not_null<History*> history) {
|
||||
}
|
||||
|
||||
ContactsBoxController::ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: PeerListController(
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation))
|
||||
, _navigation(navigation) {
|
||||
}
|
||||
|
||||
ContactsBoxController::ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
std::unique_ptr<PeerListSearchController> searchController)
|
||||
: PeerListController(std::move(searchController)) {
|
||||
: PeerListController(std::move(searchController))
|
||||
, _navigation(navigation) {
|
||||
}
|
||||
|
||||
Main::Session &ContactsBoxController::session() const {
|
||||
return _navigation->session();
|
||||
}
|
||||
|
||||
void ContactsBoxController::prepare() {
|
||||
@@ -329,7 +357,7 @@ void ContactsBoxController::prepare() {
|
||||
|
||||
prepareViewHook();
|
||||
|
||||
Auth().data().contactsLoaded().value(
|
||||
session().data().contactsLoaded().value(
|
||||
) | rpl::start_with_next([=] {
|
||||
rebuildRows();
|
||||
}, lifetime());
|
||||
@@ -349,7 +377,7 @@ void ContactsBoxController::rebuildRows() {
|
||||
}
|
||||
return count;
|
||||
};
|
||||
appendList(Auth().data().contactsList());
|
||||
appendList(session().data().contactsList());
|
||||
checkForEmptyRows();
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
@@ -357,7 +385,7 @@ void ContactsBoxController::rebuildRows() {
|
||||
void ContactsBoxController::checkForEmptyRows() {
|
||||
setDescriptionText(delegate()->peerListFullRowsCount()
|
||||
? QString()
|
||||
: Auth().data().contactsLoaded().current()
|
||||
: session().data().contactsLoaded().current()
|
||||
? tr::lng_contacts_not_found(tr::now)
|
||||
: tr::lng_contacts_loading(tr::now));
|
||||
}
|
||||
@@ -390,20 +418,30 @@ std::unique_ptr<PeerListRow> ContactsBoxController::createRow(not_null<UserData*
|
||||
return std::make_unique<PeerListRow>(user);
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::Start(not_null<UserData*> bot) {
|
||||
void AddBotToGroupBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot) {
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_cancel(), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddBotToGroupBoxController>(bot), std::move(initBox)));
|
||||
Ui::show(Box<PeerListBox>(
|
||||
std::make_unique<AddBotToGroupBoxController>(navigation, bot),
|
||||
std::move(initBox)));
|
||||
}
|
||||
|
||||
AddBotToGroupBoxController::AddBotToGroupBoxController(not_null<UserData*> bot)
|
||||
AddBotToGroupBoxController::AddBotToGroupBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot)
|
||||
: ChatsListBoxController(SharingBotGame(bot)
|
||||
? std::make_unique<PeerListGlobalSearchController>()
|
||||
? std::make_unique<PeerListGlobalSearchController>(navigation)
|
||||
: nullptr)
|
||||
, _bot(bot) {
|
||||
}
|
||||
|
||||
Main::Session &AddBotToGroupBoxController::session() const {
|
||||
return _bot->session();
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::rowClicked(not_null<PeerListRow*> row) {
|
||||
if (sharingBotGame()) {
|
||||
shareBotGame(row->peer());
|
||||
@@ -471,7 +509,7 @@ bool AddBotToGroupBoxController::needToCreateRow(
|
||||
}
|
||||
|
||||
bool AddBotToGroupBoxController::SharingBotGame(not_null<UserData*> bot) {
|
||||
auto &info = bot->botInfo;
|
||||
const auto &info = bot->botInfo;
|
||||
return (info && !info->shareGameShortName.isEmpty());
|
||||
}
|
||||
|
||||
@@ -480,7 +518,7 @@ bool AddBotToGroupBoxController::sharingBotGame() const {
|
||||
}
|
||||
|
||||
QString AddBotToGroupBoxController::emptyBoxText() const {
|
||||
return !Auth().data().chatsListLoaded()
|
||||
return !session().data().chatsListLoaded()
|
||||
? tr::lng_contacts_loading(tr::now)
|
||||
: sharingBotGame()
|
||||
? tr::lng_bot_no_chats(tr::now)
|
||||
@@ -488,7 +526,7 @@ QString AddBotToGroupBoxController::emptyBoxText() const {
|
||||
}
|
||||
|
||||
QString AddBotToGroupBoxController::noResultsText() const {
|
||||
return !Auth().data().chatsListLoaded()
|
||||
return !session().data().chatsListLoaded()
|
||||
? tr::lng_contacts_loading(tr::now)
|
||||
: sharingBotGame()
|
||||
? tr::lng_bot_chats_not_found(tr::now)
|
||||
@@ -504,7 +542,7 @@ void AddBotToGroupBoxController::prepareViewHook() {
|
||||
? tr::lng_bot_choose_chat()
|
||||
: tr::lng_bot_choose_group());
|
||||
updateLabels();
|
||||
Auth().data().chatsListLoadedEvents(
|
||||
session().data().chatsListLoadedEvents(
|
||||
) | rpl::filter([=](Data::Folder *folder) {
|
||||
return !folder;
|
||||
}) | rpl::start_with_next([=] {
|
||||
@@ -513,8 +551,15 @@ void AddBotToGroupBoxController::prepareViewHook() {
|
||||
}
|
||||
|
||||
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
FnMut<void(not_null<PeerData*>)> callback)
|
||||
: _callback(std::move(callback)) {
|
||||
: ChatsListBoxController(navigation)
|
||||
, _navigation(navigation)
|
||||
, _callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
Main::Session &ChooseRecipientBoxController::session() const {
|
||||
return _navigation->session();
|
||||
}
|
||||
|
||||
void ChooseRecipientBoxController::prepareViewHook() {
|
||||
|
||||
@@ -28,6 +28,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
//
|
||||
//};
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
class PeerListRowWithLink : public PeerListRow {
|
||||
public:
|
||||
using PeerListRow::PeerListRow;
|
||||
@@ -53,9 +57,12 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class PeerListGlobalSearchController : public PeerListSearchController, private MTP::Sender {
|
||||
class PeerListGlobalSearchController
|
||||
: public PeerListSearchController
|
||||
, private MTP::Sender {
|
||||
public:
|
||||
PeerListGlobalSearchController();
|
||||
PeerListGlobalSearchController(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
|
||||
void searchQuery(const QString &query) override;
|
||||
bool isLoading() override;
|
||||
@@ -68,6 +75,7 @@ private:
|
||||
void searchOnServer();
|
||||
void searchDone(const MTPcontacts_Found &result, mtpRequestId requestId);
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
base::Timer _timer;
|
||||
QString _query;
|
||||
mtpRequestId _requestId = 0;
|
||||
@@ -78,9 +86,9 @@ private:
|
||||
|
||||
class ChatsListBoxController : public PeerListController {
|
||||
public:
|
||||
ChatsListBoxController(not_null<Window::SessionNavigation*> navigation);
|
||||
ChatsListBoxController(
|
||||
std::unique_ptr<PeerListSearchController> searchController
|
||||
= std::make_unique<PeerListGlobalSearchController>());
|
||||
std::unique_ptr<PeerListSearchController> searchController);
|
||||
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
|
||||
@@ -114,9 +122,12 @@ private:
|
||||
class ContactsBoxController : public PeerListController {
|
||||
public:
|
||||
ContactsBoxController(
|
||||
std::unique_ptr<PeerListSearchController> searchController
|
||||
= std::make_unique<PeerListGlobalSearchController>());
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
ContactsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
std::unique_ptr<PeerListSearchController> searchController);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override final;
|
||||
std::unique_ptr<PeerListRow> createSearchRow(not_null<PeerData*> peer) override final;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
@@ -133,16 +144,23 @@ private:
|
||||
void checkForEmptyRows();
|
||||
bool appendRow(not_null<UserData*> user);
|
||||
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
|
||||
};
|
||||
|
||||
class AddBotToGroupBoxController
|
||||
: public ChatsListBoxController
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
static void Start(not_null<UserData*> bot);
|
||||
static void Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot);
|
||||
|
||||
AddBotToGroupBoxController(not_null<UserData*> bot);
|
||||
AddBotToGroupBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<UserData*> bot);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
protected:
|
||||
@@ -171,8 +189,10 @@ class ChooseRecipientBoxController
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
ChooseRecipientBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
FnMut<void(not_null<PeerData*>)> callback);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
bool respectSavedMessagesChat() const override {
|
||||
@@ -185,6 +205,7 @@ protected:
|
||||
not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
FnMut<void(not_null<PeerData*>)> _callback;
|
||||
|
||||
};
|
||||
|
||||
@@ -17,7 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_folder.h"
|
||||
#include "history/history.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "auth_session.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "main/main_session.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/window_session_controller.h"
|
||||
@@ -46,20 +47,29 @@ base::flat_set<not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
||||
|
||||
} // namespace
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController()
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation)
|
||||
: ContactsBoxController(
|
||||
std::make_unique<PeerListGlobalSearchController>()) {
|
||||
navigation,
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation)) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer)
|
||||
: AddParticipantsBoxController(peer, GetAlreadyInFromPeer(peer)) {
|
||||
: AddParticipantsBoxController(
|
||||
navigation,
|
||||
peer,
|
||||
GetAlreadyInFromPeer(peer)) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
: ContactsBoxController(
|
||||
navigation,
|
||||
std::make_unique<PeerListGlobalSearchController>(navigation))
|
||||
, _peer(peer)
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
subscribeToMigration();
|
||||
@@ -179,8 +189,12 @@ bool AddParticipantsBoxController::inviteSelectedUsers(
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(not_null<ChatData*> chat) {
|
||||
auto controller = std::make_unique<AddParticipantsBoxController>(chat);
|
||||
void AddParticipantsBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChatData*> chat) {
|
||||
auto controller = std::make_unique<AddParticipantsBoxController>(
|
||||
navigation,
|
||||
chat);
|
||||
const auto weak = controller.get();
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->addButton(tr::lng_participant_invite(), [=] {
|
||||
@@ -198,10 +212,12 @@ void AddParticipantsBoxController::Start(not_null<ChatData*> chat) {
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated) {
|
||||
auto controller = std::make_unique<AddParticipantsBoxController>(
|
||||
navigation,
|
||||
channel,
|
||||
std::move(alreadyIn));
|
||||
const auto weak = controller.get();
|
||||
@@ -237,13 +253,16 @@ void AddParticipantsBoxController::Start(
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn) {
|
||||
Start(channel, std::move(alreadyIn), false);
|
||||
Start(navigation, channel, std::move(alreadyIn), false);
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(not_null<ChannelData*> channel) {
|
||||
Start(channel, {}, true);
|
||||
void AddParticipantsBoxController::Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel) {
|
||||
Start(navigation, channel, {}, true);
|
||||
}
|
||||
|
||||
AddSpecialBoxController::AddSpecialBoxController(
|
||||
@@ -262,6 +281,10 @@ AddSpecialBoxController::AddSpecialBoxController(
|
||||
subscribeToMigration();
|
||||
}
|
||||
|
||||
Main::Session &AddSpecialBoxController::session() const {
|
||||
return _peer->session();
|
||||
}
|
||||
|
||||
void AddSpecialBoxController::subscribeToMigration() {
|
||||
SubscribeToMigration(
|
||||
_peer,
|
||||
@@ -276,7 +299,7 @@ void AddSpecialBoxController::migrate(not_null<ChannelData*> channel) {
|
||||
|
||||
std::unique_ptr<PeerListRow> AddSpecialBoxController::createSearchRow(
|
||||
not_null<PeerData*> peer) {
|
||||
if (peer->isSelf()) {
|
||||
if (_excludeSelf && peer->isSelf()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (const auto user = peer->asUser()) {
|
||||
@@ -289,6 +312,8 @@ void AddSpecialBoxController::prepare() {
|
||||
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
|
||||
auto title = [&] {
|
||||
switch (_role) {
|
||||
case Role::Members:
|
||||
return tr::lng_profile_participants_section();
|
||||
case Role::Admins:
|
||||
return tr::lng_channel_add_admin();
|
||||
case Role::Restricted:
|
||||
@@ -552,11 +577,16 @@ void AddSpecialBoxController::showAdmin(
|
||||
: adminRights
|
||||
? *adminRights
|
||||
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
||||
auto box = Box<EditAdminBox>(_peer, user, currentRights);
|
||||
auto box = Box<EditAdminBox>(
|
||||
_peer,
|
||||
user,
|
||||
currentRights,
|
||||
_additional.adminRank(user));
|
||||
if (_additional.canAddOrEditAdmin(user)) {
|
||||
const auto done = crl::guard(this, [=](
|
||||
const MTPChatAdminRights &newRights) {
|
||||
editAdminDone(user, newRights);
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank) {
|
||||
editAdminDone(user, newRights, rank);
|
||||
});
|
||||
const auto fail = crl::guard(this, [=] {
|
||||
if (_editParticipantBox) {
|
||||
@@ -570,30 +600,40 @@ void AddSpecialBoxController::showAdmin(
|
||||
|
||||
void AddSpecialBoxController::editAdminDone(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights) {
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank) {
|
||||
if (_editParticipantBox) {
|
||||
_editParticipantBox->closeBox();
|
||||
}
|
||||
|
||||
const auto date = unixtime(); // Incorrect, but ignored.
|
||||
if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
const auto date = base::unixtime::now(); // Incorrect, but ignored.
|
||||
if (_additional.isCreator(user) && user->isSelf()) {
|
||||
using Flag = MTPDchannelParticipantCreator::Flag;
|
||||
_additional.applyParticipant(MTP_channelParticipantCreator(
|
||||
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
|
||||
MTP_int(user->bareId()),
|
||||
MTP_string(rank)));
|
||||
} else if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
MTP_int(date)));
|
||||
} else {
|
||||
using Flag = MTPDchannelParticipantAdmin::Flag;
|
||||
const auto alreadyPromotedBy = _additional.adminPromotedBy(user);
|
||||
_additional.applyParticipant(MTP_channelParticipantAdmin(
|
||||
MTP_flags(MTPDchannelParticipantAdmin::Flag::f_can_edit),
|
||||
MTP_flags(Flag::f_can_edit
|
||||
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
|
||||
MTP_int(user->bareId()),
|
||||
MTPint(), // inviter_id
|
||||
MTP_int(alreadyPromotedBy
|
||||
? alreadyPromotedBy->bareId()
|
||||
: user->session().userId()),
|
||||
MTP_int(date),
|
||||
rights));
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
}
|
||||
if (const auto callback = _adminDoneCallback) {
|
||||
callback(user, rights);
|
||||
callback(user, rights, rank);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,7 +712,7 @@ void AddSpecialBoxController::editRestrictedDone(
|
||||
_editParticipantBox->closeBox();
|
||||
}
|
||||
|
||||
const auto date = unixtime(); // Incorrect, but ignored.
|
||||
const auto date = base::unixtime::now(); // Incorrect, but ignored.
|
||||
if (rights.c_chatBannedRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
@@ -761,7 +801,8 @@ void AddSpecialBoxController::kickUser(
|
||||
}
|
||||
|
||||
bool AddSpecialBoxController::appendRow(not_null<UserData*> user) {
|
||||
if (delegate()->peerListFindRow(user->id) || user->isSelf()) {
|
||||
if (delegate()->peerListFindRow(user->id)
|
||||
|| (_excludeSelf && user->isSelf())) {
|
||||
return false;
|
||||
}
|
||||
delegate()->peerListAppendRow(createRow(user));
|
||||
|
||||
@@ -10,17 +10,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/peers/edit_participants_box.h"
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
class AddParticipantsBoxController : public ContactsBoxController {
|
||||
public:
|
||||
static void Start(not_null<ChatData*> chat);
|
||||
static void Start(not_null<ChannelData*> channel);
|
||||
static void Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChatData*> chat);
|
||||
static void Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel);
|
||||
static void Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
AddParticipantsBoxController();
|
||||
AddParticipantsBoxController(not_null<PeerData*> peer);
|
||||
explicit AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation);
|
||||
AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer);
|
||||
AddParticipantsBoxController(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
@@ -34,6 +47,7 @@ protected:
|
||||
|
||||
private:
|
||||
static void Start(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated);
|
||||
@@ -62,7 +76,8 @@ public:
|
||||
|
||||
using AdminDoneCallback = Fn<void(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &adminRights)>;
|
||||
const MTPChatAdminRights &adminRights,
|
||||
const QString &rank)>;
|
||||
using BannedDoneCallback = Fn<void(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatBannedRights &bannedRights)>;
|
||||
@@ -72,6 +87,7 @@ public:
|
||||
AdminDoneCallback adminDoneCallback,
|
||||
BannedDoneCallback bannedDoneCallback);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
void loadMoreRows() override;
|
||||
@@ -89,7 +105,8 @@ private:
|
||||
void showAdmin(not_null<UserData*> user, bool sure = false);
|
||||
void editAdminDone(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights);
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank);
|
||||
void showRestricted(not_null<UserData*> user, bool sure = false);
|
||||
void editRestrictedDone(
|
||||
not_null<UserData*> user,
|
||||
@@ -114,6 +131,9 @@ private:
|
||||
AdminDoneCallback _adminDoneCallback;
|
||||
BannedDoneCallback _bannedDoneCallback;
|
||||
|
||||
protected:
|
||||
bool _excludeSelf = true;
|
||||
|
||||
};
|
||||
|
||||
// Finds chat/channel members, then contacts, then global search results.
|
||||
|
||||
@@ -18,13 +18,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxUserFirstLastName = 64; // See also add_contact_box.
|
||||
|
||||
QString UserPhone(not_null<UserData*> user) {
|
||||
const auto phone = user->phone();
|
||||
return phone.isEmpty()
|
||||
@@ -220,6 +222,8 @@ void Controller::initNameFields(
|
||||
};
|
||||
QObject::connect(first, &Ui::InputField::submitted, submit);
|
||||
QObject::connect(last, &Ui::InputField::submitted, submit);
|
||||
first->setMaxLength(kMaxUserFirstLastName);
|
||||
first->setMaxLength(kMaxUserFirstLastName);
|
||||
}
|
||||
|
||||
void Controller::setupWarning() {
|
||||
|
||||
@@ -18,7 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
const std::vector<not_null<PeerData*>> &chats,
|
||||
Fn<void(ChannelData*)> callback);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
int contentWidth() const override;
|
||||
@@ -69,6 +70,10 @@ Controller::Controller(
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
Main::Session &Controller::session() const {
|
||||
return _channel->session();
|
||||
}
|
||||
|
||||
int Controller::contentWidth() const {
|
||||
return st::boxWidth;
|
||||
}
|
||||
@@ -232,6 +237,7 @@ object_ptr<Ui::RpWidget> SetupFooter(
|
||||
|
||||
object_ptr<Ui::RpWidget> SetupCreateGroup(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
Fn<void(ChannelData*)> callback) {
|
||||
Expects(channel->isBroadcast());
|
||||
@@ -245,6 +251,7 @@ object_ptr<Ui::RpWidget> SetupCreateGroup(
|
||||
const auto guarded = crl::guard(parent, callback);
|
||||
Ui::show(
|
||||
Box<GroupInfoBox>(
|
||||
navigation,
|
||||
GroupInfoBox::Type::Megagroup,
|
||||
channel->name + " Chat",
|
||||
guarded),
|
||||
@@ -270,6 +277,7 @@ object_ptr<Ui::RpWidget> SetupUnlink(
|
||||
}
|
||||
|
||||
object_ptr<BoxContent> EditLinkedChatBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
ChannelData *chat,
|
||||
std::vector<not_null<PeerData*>> &&chats,
|
||||
@@ -283,7 +291,11 @@ object_ptr<BoxContent> EditLinkedChatBox(
|
||||
SetupAbout(above, channel, chat),
|
||||
st::linkedChatAboutPadding);
|
||||
if (!chat) {
|
||||
above->add(SetupCreateGroup(above, channel, callback));
|
||||
above->add(SetupCreateGroup(
|
||||
above,
|
||||
navigation,
|
||||
channel,
|
||||
callback));
|
||||
}
|
||||
box->peerListSetAboveWidget(std::move(above));
|
||||
|
||||
@@ -312,10 +324,12 @@ object_ptr<BoxContent> EditLinkedChatBox(
|
||||
} // namespace
|
||||
|
||||
object_ptr<BoxContent> EditLinkedChatBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
std::vector<not_null<PeerData*>> &&chats,
|
||||
Fn<void(ChannelData*)> callback) {
|
||||
return EditLinkedChatBox(
|
||||
navigation,
|
||||
channel,
|
||||
nullptr,
|
||||
std::move(chats),
|
||||
@@ -324,9 +338,16 @@ object_ptr<BoxContent> EditLinkedChatBox(
|
||||
}
|
||||
|
||||
object_ptr<BoxContent> EditLinkedChatBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<ChannelData*> chat,
|
||||
bool canEdit,
|
||||
Fn<void(ChannelData*)> callback) {
|
||||
return EditLinkedChatBox(channel, chat, {}, canEdit, callback);
|
||||
return EditLinkedChatBox(
|
||||
navigation,
|
||||
channel,
|
||||
chat,
|
||||
{},
|
||||
canEdit,
|
||||
callback);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
object_ptr<BoxContent> EditLinkedChatBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<ChannelData*> chat,
|
||||
bool canEdit,
|
||||
Fn<void(ChannelData*)> callback);
|
||||
|
||||
object_ptr<BoxContent> EditLinkedChatBox(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
std::vector<not_null<PeerData*>> &&chats,
|
||||
Fn<void(ChannelData*)> callback);
|
||||
|
||||
@@ -14,10 +14,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/text_options.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "info/profile/info_profile_button.h"
|
||||
#include "settings/settings_privacy_security.h"
|
||||
#include "boxes/calendar_box.h"
|
||||
@@ -31,8 +33,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "core/core_cloud_password.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
@@ -41,6 +44,7 @@ namespace {
|
||||
constexpr auto kMaxRestrictDelayDays = 366;
|
||||
constexpr auto kSecondsInDay = 24 * 60 * 60;
|
||||
constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
|
||||
constexpr auto kAdminRoleLimit = 16;
|
||||
|
||||
enum class PasswordErrorType {
|
||||
None,
|
||||
@@ -53,7 +57,7 @@ void SetCloudPassword(not_null<GenericBox*> box, not_null<UserData*> user) {
|
||||
) | rpl::start_with_next([=] {
|
||||
using namespace Settings;
|
||||
const auto weak = make_weak(box);
|
||||
if (CheckEditCloudPassword()) {
|
||||
if (CheckEditCloudPassword(&user->session())) {
|
||||
box->getDelegate()->show(
|
||||
EditCloudPasswordBox(&user->session()));
|
||||
} else {
|
||||
@@ -195,23 +199,23 @@ void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
st::rightsPhotoMargin.top() + st::rightsNameTop,
|
||||
namew,
|
||||
width());
|
||||
auto statusText = [this] {
|
||||
if (_user->botInfo) {
|
||||
const auto statusText = [&] {
|
||||
if (_user->isBot()) {
|
||||
const auto seesAllMessages = _user->botInfo->readsAllHistory
|
||||
|| _hasAdminRights;
|
||||
return (seesAllMessages
|
||||
? tr::lng_status_bot_reads_all
|
||||
: tr::lng_status_bot_not_reads_all)(tr::now);
|
||||
}
|
||||
return Data::OnlineText(_user->onlineTill, unixtime());
|
||||
};
|
||||
return Data::OnlineText(_user->onlineTill, base::unixtime::now());
|
||||
}();
|
||||
p.setFont(st::contactsStatusFont);
|
||||
p.setPen(st::contactsStatusFg);
|
||||
p.drawTextLeft(
|
||||
namex,
|
||||
st::rightsPhotoMargin.top() + st::rightsStatusTop,
|
||||
width(),
|
||||
statusText());
|
||||
statusText);
|
||||
}
|
||||
|
||||
EditParticipantBox::EditParticipantBox(
|
||||
@@ -242,17 +246,28 @@ Widget *EditParticipantBox::addControl(
|
||||
return _inner->addControl(std::move(widget), margin);
|
||||
}
|
||||
|
||||
bool EditParticipantBox::amCreator() const {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
return chat->amCreator();
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
return channel->amCreator();
|
||||
}
|
||||
Unexpected("Peer type in EditParticipantBox::Inner::amCreator.");
|
||||
}
|
||||
|
||||
EditAdminBox::EditAdminBox(
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights)
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank)
|
||||
: EditParticipantBox(
|
||||
nullptr,
|
||||
peer,
|
||||
user,
|
||||
(rights.c_chatAdminRights().vflags().v != 0))
|
||||
, _oldRights(rights) {
|
||||
, _oldRights(rights)
|
||||
, _oldRank(rank) {
|
||||
}
|
||||
|
||||
MTPChatAdminRights EditAdminBox::Defaults(not_null<PeerData*> peer) {
|
||||
@@ -302,7 +317,7 @@ void EditAdminBox::prepare() {
|
||||
|
||||
const auto disabledMessages = [&] {
|
||||
auto result = std::map<Flags, QString>();
|
||||
if (!canSave()) {
|
||||
if (!canSave() || (amCreator() && user()->isSelf())) {
|
||||
result.emplace(
|
||||
~Flags(0),
|
||||
tr::lng_rights_about_admin_cant_edit(tr::now));
|
||||
@@ -339,6 +354,18 @@ void EditAdminBox::prepare() {
|
||||
) | rpl::then(std::move(
|
||||
changes
|
||||
));
|
||||
_aboutAddAdmins = addControl(
|
||||
object_ptr<Ui::FlatLabel>(this, st::boxDividerLabel),
|
||||
st::rightsAboutMargin);
|
||||
rpl::duplicate(
|
||||
selectedFlags
|
||||
) | rpl::map(
|
||||
(_1 & Flag::f_add_admins) != 0
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
refreshAboutAddAdminsText(checked);
|
||||
}, lifetime());
|
||||
|
||||
if (canTransferOwnership()) {
|
||||
const auto allFlags = FullAdminRights(isGroup);
|
||||
setupTransferButton(
|
||||
@@ -349,19 +376,12 @@ void EditAdminBox::prepare() {
|
||||
((_1 & allFlags) == allFlags)
|
||||
))->setDuration(0);
|
||||
}
|
||||
_aboutAddAdmins = addControl(
|
||||
object_ptr<Ui::FlatLabel>(this, st::boxLabel),
|
||||
st::rightsAboutMargin);
|
||||
std::move(
|
||||
selectedFlags
|
||||
) | rpl::map(
|
||||
(_1 & Flag::f_add_admins) != 0
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool checked) {
|
||||
refreshAboutAddAdminsText(checked);
|
||||
}, lifetime());
|
||||
|
||||
if (canSave()) {
|
||||
const auto rank = (chat || channel->isMegagroup())
|
||||
? addRankInput().get()
|
||||
: nullptr;
|
||||
|
||||
addButton(tr::lng_settings_save(), [=, value = getChecked] {
|
||||
if (!_saveCallback) {
|
||||
return;
|
||||
@@ -372,14 +392,66 @@ void EditAdminBox::prepare() {
|
||||
: channel->adminRights());
|
||||
_saveCallback(
|
||||
_oldRights,
|
||||
MTP_chatAdminRights(MTP_flags(newFlags)));
|
||||
MTP_chatAdminRights(MTP_flags(newFlags)),
|
||||
rank ? rank->getLastText().trimmed() : QString());
|
||||
});
|
||||
addButton(tr::lng_cancel(), [this] { closeBox(); });
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
} else {
|
||||
addButton(tr::lng_box_ok(), [this] { closeBox(); });
|
||||
addButton(tr::lng_box_ok(), [=] { closeBox(); });
|
||||
}
|
||||
}
|
||||
|
||||
not_null<Ui::InputField*> EditAdminBox::addRankInput() {
|
||||
addControl(
|
||||
object_ptr<BoxContentDivider>(this),
|
||||
st::rightsRankMargin);
|
||||
|
||||
addControl(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
tr::lng_rights_edit_admin_rank_name(),
|
||||
st::rightsHeaderLabel),
|
||||
st::rightsHeaderMargin);
|
||||
|
||||
const auto isOwner = [&] {
|
||||
if (user()->isSelf() && amCreator()) {
|
||||
return true;
|
||||
} else if (const auto chat = peer()->asChat()) {
|
||||
return chat->creator == peerToUser(user()->id);
|
||||
} else if (const auto channel = peer()->asChannel()) {
|
||||
return channel->mgInfo && channel->mgInfo->creator == user();
|
||||
}
|
||||
Unexpected("Peer type in EditAdminBox::addRankInput.");
|
||||
}();
|
||||
const auto result = addControl(
|
||||
object_ptr<Ui::InputField>(
|
||||
this,
|
||||
st::customBadgeField,
|
||||
(isOwner ? tr::lng_owner_badge : tr::lng_admin_badge)(),
|
||||
TextUtilities::RemoveEmoji(_oldRank)),
|
||||
st::rightsAboutMargin);
|
||||
result->setMaxLength(kAdminRoleLimit);
|
||||
result->setInstantReplaces(Ui::InstantReplaces::TextOnly());
|
||||
connect(result, &Ui::InputField::changed, [=] {
|
||||
const auto text = result->getLastText();
|
||||
const auto removed = TextUtilities::RemoveEmoji(text);
|
||||
if (removed != text) {
|
||||
result->setText(removed);
|
||||
}
|
||||
});
|
||||
|
||||
addControl(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
this,
|
||||
tr::lng_rights_edit_admin_rank_about(
|
||||
lt_title,
|
||||
(isOwner ? tr::lng_owner_badge : tr::lng_admin_badge)()),
|
||||
st::boxDividerLabel),
|
||||
st::rightsAboutMargin);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EditAdminBox::canTransferOwnership() const {
|
||||
if (user()->isInaccessible() || user()->isBot() || user()->isSelf()) {
|
||||
return false;
|
||||
@@ -399,13 +471,10 @@ not_null<Ui::SlideWrap<Ui::RpWidget>*> EditAdminBox::setupTransferButton(
|
||||
object_ptr<Ui::VerticalLayout>(this)));
|
||||
|
||||
const auto container = wrap->entity();
|
||||
const auto addDivider = [&] {
|
||||
container->add(
|
||||
object_ptr<BoxContentDivider>(container),
|
||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||
};
|
||||
|
||||
addDivider();
|
||||
container->add(
|
||||
object_ptr<BoxContentDivider>(container),
|
||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||
container->add(EditPeerInfoBox::CreateButton(
|
||||
this,
|
||||
(isGroup
|
||||
@@ -414,7 +483,6 @@ not_null<Ui::SlideWrap<Ui::RpWidget>*> EditAdminBox::setupTransferButton(
|
||||
rpl::single(QString()),
|
||||
[=] { transferOwnership(); },
|
||||
st::peerPermissionsButton));
|
||||
addDivider();
|
||||
|
||||
return wrap;
|
||||
}
|
||||
@@ -498,7 +566,9 @@ void EditAdminBox::requestTransferPassword(not_null<ChannelData*> channel) {
|
||||
const Core::CloudPasswordResult &result) {
|
||||
sendTransferRequestFrom(*box, channel, result);
|
||||
});
|
||||
*box = getDelegate()->show(Box<PasscodeBox>(fields));
|
||||
*box = getDelegate()->show(Box<PasscodeBox>(
|
||||
&channel->session(),
|
||||
fields));
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
@@ -568,7 +638,7 @@ void EditAdminBox::sendTransferRequestFrom(
|
||||
|
||||
void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
|
||||
_aboutAddAdmins->setText([&] {
|
||||
if (!canSave()) {
|
||||
if (!canSave() || (amCreator() && user()->isSelf())) {
|
||||
return tr::lng_rights_about_admin_cant_edit(tr::now);
|
||||
} else if (canAddAdmins) {
|
||||
return tr::lng_rights_about_add_admins_yes(tr::now);
|
||||
@@ -678,7 +748,7 @@ void EditRestrictedBox::showRestrictUntil() {
|
||||
auto tomorrow = QDate::currentDate().addDays(1);
|
||||
auto highlighted = isUntilForever()
|
||||
? tomorrow
|
||||
: ParseDateTime(getRealUntilValue()).date();
|
||||
: base::unixtime::parse(getRealUntilValue()).date();
|
||||
auto month = highlighted;
|
||||
_restrictUntilBox = Ui::show(
|
||||
Box<CalendarBox>(
|
||||
@@ -751,7 +821,8 @@ void EditRestrictedBox::createUntilVariants() {
|
||||
tr::lng_rights_chat_banned_custom_date(
|
||||
tr::now,
|
||||
lt_date,
|
||||
langDayOfMonthFull(ParseDateTime(until).date())));
|
||||
langDayOfMonthFull(
|
||||
base::unixtime::parse(until).date())));
|
||||
}
|
||||
};
|
||||
auto addCurrentVariant = [&](TimeId from, TimeId to) {
|
||||
@@ -766,7 +837,7 @@ void EditRestrictedBox::createUntilVariants() {
|
||||
};
|
||||
addVariant(0, tr::lng_rights_chat_banned_forever(tr::now));
|
||||
|
||||
auto now = unixtime();
|
||||
auto now = base::unixtime::now();
|
||||
auto nextDay = now + kSecondsInDay;
|
||||
auto nextWeek = now + kSecondsInWeek;
|
||||
addCurrentVariant(0, nextDay);
|
||||
@@ -780,9 +851,9 @@ void EditRestrictedBox::createUntilVariants() {
|
||||
TimeId EditRestrictedBox::getRealUntilValue() const {
|
||||
Expects(_until != kUntilCustom);
|
||||
if (_until == kUntilOneDay) {
|
||||
return unixtime() + kSecondsInDay;
|
||||
return base::unixtime::now() + kSecondsInDay;
|
||||
} else if (_until == kUntilOneWeek) {
|
||||
return unixtime() + kSecondsInWeek;
|
||||
return base::unixtime::now() + kSecondsInWeek;
|
||||
}
|
||||
Assert(_until >= 0);
|
||||
return _until;
|
||||
|
||||
@@ -38,12 +38,13 @@ public:
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
not_null<UserData*> user() const {
|
||||
[[nodiscard]] not_null<UserData*> user() const {
|
||||
return _user;
|
||||
}
|
||||
not_null<PeerData*> peer() const {
|
||||
[[nodiscard]] not_null<PeerData*> peer() const {
|
||||
return _peer;
|
||||
}
|
||||
[[nodiscard]] bool amCreator() const;
|
||||
|
||||
template <typename Widget>
|
||||
Widget *addControl(object_ptr<Widget> widget, QMargins margin = {});
|
||||
@@ -68,10 +69,14 @@ public:
|
||||
QWidget*,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights);
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank);
|
||||
|
||||
void setSaveCallback(
|
||||
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> callback) {
|
||||
Fn<void(
|
||||
MTPChatAdminRights,
|
||||
MTPChatAdminRights,
|
||||
const QString &rank)> callback) {
|
||||
_saveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -84,6 +89,7 @@ private:
|
||||
|
||||
static MTPChatAdminRights Defaults(not_null<PeerData*> peer);
|
||||
|
||||
not_null<Ui::InputField*> addRankInput();
|
||||
void transferOwnership();
|
||||
void transferOwnershipChecked();
|
||||
bool handleTransferPasswordError(const RPCError &error);
|
||||
@@ -93,14 +99,18 @@ private:
|
||||
not_null<ChannelData*> channel,
|
||||
const Core::CloudPasswordResult &result);
|
||||
bool canSave() const {
|
||||
return !!_saveCallback;
|
||||
return _saveCallback != nullptr;
|
||||
}
|
||||
void refreshAboutAddAdminsText(bool canAddAdmins);
|
||||
bool canTransferOwnership() const;
|
||||
not_null<Ui::SlideWrap<Ui::RpWidget>*> setupTransferButton(bool isGroup);
|
||||
|
||||
const MTPChatAdminRights _oldRights;
|
||||
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> _saveCallback;
|
||||
const QString _oldRank;
|
||||
Fn<void(
|
||||
MTPChatAdminRights,
|
||||
MTPChatAdminRights,
|
||||
const QString &rank)> _saveCallback;
|
||||
|
||||
QPointer<Ui::FlatLabel> _aboutAddAdmins;
|
||||
mtpRequestId _checkTransferRequestId = 0;
|
||||
|
||||
@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/add_participants_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "history/history.h"
|
||||
@@ -47,10 +48,11 @@ void RemoveAdmin(
|
||||
channel->session().api().request(MTPchannels_EditAdmin(
|
||||
channel->inputChannel,
|
||||
user->inputUser,
|
||||
newRights
|
||||
newRights,
|
||||
MTP_string(QString())
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
channel->session().api().applyUpdates(result);
|
||||
channel->applyEditAdmin(user, oldRights, newRights);
|
||||
channel->applyEditAdmin(user, oldRights, newRights, QString());
|
||||
if (onDone) {
|
||||
onDone();
|
||||
}
|
||||
@@ -118,15 +120,17 @@ void SaveChannelAdmin(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &oldRights,
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank,
|
||||
Fn<void()> onDone,
|
||||
Fn<void()> onFail) {
|
||||
channel->session().api().request(MTPchannels_EditAdmin(
|
||||
channel->inputChannel,
|
||||
user->inputUser,
|
||||
newRights
|
||||
newRights,
|
||||
MTP_string(rank)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
channel->session().api().applyUpdates(result);
|
||||
channel->applyEditAdmin(user, oldRights, newRights);
|
||||
channel->applyEditAdmin(user, oldRights, newRights, rank);
|
||||
if (onDone) {
|
||||
onDone();
|
||||
}
|
||||
@@ -186,21 +190,26 @@ void SaveChatParticipantKick(
|
||||
|
||||
Fn<void(
|
||||
const MTPChatAdminRights &oldRights,
|
||||
const MTPChatAdminRights &newRights)> SaveAdminCallback(
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank)> SaveAdminCallback(
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user,
|
||||
Fn<void(const MTPChatAdminRights &newRights)> onDone,
|
||||
Fn<void(
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank)> onDone,
|
||||
Fn<void()> onFail) {
|
||||
return [=](
|
||||
const MTPChatAdminRights &oldRights,
|
||||
const MTPChatAdminRights &newRights) {
|
||||
const auto done = [=] { if (onDone) onDone(newRights); };
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank) {
|
||||
const auto done = [=] { if (onDone) onDone(newRights, rank); };
|
||||
const auto saveForChannel = [=](not_null<ChannelData*> channel) {
|
||||
SaveChannelAdmin(
|
||||
channel,
|
||||
user,
|
||||
oldRights,
|
||||
newRights,
|
||||
rank,
|
||||
done,
|
||||
onFail);
|
||||
};
|
||||
@@ -212,7 +221,7 @@ Fn<void(
|
||||
const MTPDchatAdminRights &data) {
|
||||
return data.vflags().v;
|
||||
});
|
||||
if (flags == ChatData::DefaultAdminRights()) {
|
||||
if (flags == ChatData::DefaultAdminRights() && rank.isEmpty()) {
|
||||
saveChatAdmin(true);
|
||||
} else if (!flags) {
|
||||
saveChatAdmin(false);
|
||||
@@ -309,10 +318,10 @@ bool ParticipantsAdditionalData::infoLoaded(
|
||||
|
||||
bool ParticipantsAdditionalData::canEditAdmin(
|
||||
not_null<UserData*> user) const {
|
||||
if (_creator == user || user->isSelf()) {
|
||||
return false;
|
||||
} else if (_creator && _creator->isSelf()) {
|
||||
if (_creator && _creator->isSelf()) {
|
||||
return true;
|
||||
} else if (_creator == user || user->isSelf()) {
|
||||
return false;
|
||||
} else if (adminRights(user).has_value()) {
|
||||
return !_peer->isChat() && _adminCanEdit.contains(user);
|
||||
}
|
||||
@@ -333,7 +342,7 @@ bool ParticipantsAdditionalData::canAddOrEditAdmin(
|
||||
|
||||
bool ParticipantsAdditionalData::canRestrictUser(
|
||||
not_null<UserData*> user) const {
|
||||
if (!canEditAdmin(user)) {
|
||||
if (!canEditAdmin(user) || user->isSelf()) {
|
||||
return false;
|
||||
} else if (const auto chat = _peer->asChat()) {
|
||||
return chat->canBanMembers();
|
||||
@@ -358,6 +367,12 @@ auto ParticipantsAdditionalData::adminRights(
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
QString ParticipantsAdditionalData::adminRank(
|
||||
not_null<UserData*> user) const {
|
||||
const auto i = _adminRanks.find(user);
|
||||
return (i != end(_adminRanks)) ? i->second : QString();
|
||||
}
|
||||
|
||||
auto ParticipantsAdditionalData::restrictedRights(
|
||||
not_null<UserData*> user) const
|
||||
-> std::optional<MTPChatBannedRights> {
|
||||
@@ -452,9 +467,11 @@ void ParticipantsAdditionalData::fillFromChannel(
|
||||
}
|
||||
if (information->creator) {
|
||||
_creator = information->creator;
|
||||
_adminRanks[information->creator] = information->creatorRank;
|
||||
}
|
||||
for (const auto user : information->lastParticipants) {
|
||||
const auto admin = information->lastAdmins.find(user);
|
||||
const auto rank = information->admins.find(peerToUser(user->id));
|
||||
const auto restricted = information->lastRestricted.find(user);
|
||||
if (admin != information->lastAdmins.cend()) {
|
||||
_restrictedRights.erase(user);
|
||||
@@ -466,6 +483,10 @@ void ParticipantsAdditionalData::fillFromChannel(
|
||||
_adminCanEdit.erase(user);
|
||||
}
|
||||
_adminRights.emplace(user, admin->second.rights);
|
||||
if (rank != end(information->admins)
|
||||
&& !rank->second.isEmpty()) {
|
||||
_adminRanks[user] = rank->second;
|
||||
}
|
||||
} else if (restricted != information->lastRestricted.cend()) {
|
||||
_adminRights.erase(user);
|
||||
_adminCanEdit.erase(user);
|
||||
@@ -532,6 +553,11 @@ UserData *ParticipantsAdditionalData::applyCreator(
|
||||
const MTPDchannelParticipantCreator &data) {
|
||||
if (const auto user = applyRegular(data.vuser_id())) {
|
||||
_creator = user;
|
||||
if (const auto rank = data.vrank()) {
|
||||
_adminRanks[user] = qs(*rank);
|
||||
} else {
|
||||
_adminRanks.remove(user);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -558,6 +584,11 @@ UserData *ParticipantsAdditionalData::applyAdmin(
|
||||
} else {
|
||||
_adminCanEdit.erase(user);
|
||||
}
|
||||
if (const auto rank = data.vrank()) {
|
||||
_adminRanks[user] = qs(*rank);
|
||||
} else {
|
||||
_adminRanks.remove(user);
|
||||
}
|
||||
if (const auto by = _peer->owner().userLoaded(data.vpromoted_by().v)) {
|
||||
const auto i = _adminPromotedBy.find(user);
|
||||
if (i == _adminPromotedBy.end()) {
|
||||
@@ -671,7 +702,7 @@ void ParticipantsOnlineSorter::sort() {
|
||||
_onlineCount = 0;
|
||||
return;
|
||||
}
|
||||
const auto now = unixtime();
|
||||
const auto now = base::unixtime::now();
|
||||
_delegate->peerListSortRows([&](
|
||||
const PeerListRow &a,
|
||||
const PeerListRow &b) {
|
||||
@@ -686,7 +717,7 @@ rpl::producer<int> ParticipantsOnlineSorter::onlineCountValue() const {
|
||||
}
|
||||
|
||||
void ParticipantsOnlineSorter::refreshOnlineCount() {
|
||||
const auto now = unixtime();
|
||||
const auto now = base::unixtime::now();
|
||||
auto left = 0, right = _delegate->peerListFullRowsCount();
|
||||
while (right > left) {
|
||||
const auto middle = (left + right) / 2;
|
||||
@@ -723,6 +754,10 @@ ParticipantsBoxController::ParticipantsBoxController(
|
||||
}
|
||||
}
|
||||
|
||||
Main::Session &ParticipantsBoxController::session() const {
|
||||
return _peer->session();
|
||||
}
|
||||
|
||||
void ParticipantsBoxController::setupListChangeViewers() {
|
||||
const auto channel = _peer->asChannel();
|
||||
if (!channel || !channel->isMegagroup()) {
|
||||
@@ -849,8 +884,9 @@ void ParticipantsBoxController::addNewItem() {
|
||||
}
|
||||
const auto adminDone = crl::guard(this, [=](
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights) {
|
||||
editAdminDone(user, rights);
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank) {
|
||||
editAdminDone(user, rights, rank);
|
||||
});
|
||||
const auto restrictedDone = crl::guard(this, [=](
|
||||
not_null<UserData*> user,
|
||||
@@ -876,7 +912,7 @@ void ParticipantsBoxController::addNewParticipants() {
|
||||
const auto chat = _peer->asChat();
|
||||
const auto channel = _peer->asChannel();
|
||||
if (chat) {
|
||||
AddParticipantsBoxController::Start(chat);
|
||||
AddParticipantsBoxController::Start(_navigation, chat);
|
||||
} else if (channel->isMegagroup()
|
||||
|| channel->membersCount() < Global::ChatSizeMax()) {
|
||||
const auto count = delegate()->peerListFullRowsCount();
|
||||
@@ -887,6 +923,7 @@ void ParticipantsBoxController::addNewParticipants() {
|
||||
delegate()->peerListRowAt(i)->peer()->asUser());
|
||||
}
|
||||
AddParticipantsBoxController::Start(
|
||||
_navigation,
|
||||
channel,
|
||||
{ already.begin(), already.end() });
|
||||
} else {
|
||||
@@ -1415,13 +1452,18 @@ void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
|
||||
: adminRights
|
||||
? *adminRights
|
||||
: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0)));
|
||||
auto box = Box<EditAdminBox>(_peer, user, currentRights);
|
||||
auto box = Box<EditAdminBox>(
|
||||
_peer,
|
||||
user,
|
||||
currentRights,
|
||||
_additional.adminRank(user));
|
||||
const auto chat = _peer->asChat();
|
||||
const auto channel = _peer->asChannel();
|
||||
if (_additional.canAddOrEditAdmin(user)) {
|
||||
const auto done = crl::guard(this, [=](
|
||||
const MTPChatAdminRights &newRights) {
|
||||
editAdminDone(user, newRights);
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank) {
|
||||
editAdminDone(user, newRights, rank);
|
||||
});
|
||||
const auto fail = crl::guard(this, [=] {
|
||||
if (_editParticipantBox) {
|
||||
@@ -1435,14 +1477,21 @@ void ParticipantsBoxController::showAdmin(not_null<UserData*> user) {
|
||||
|
||||
void ParticipantsBoxController::editAdminDone(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights) {
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank) {
|
||||
_addBox = nullptr;
|
||||
if (_editParticipantBox) {
|
||||
_editParticipantBox->closeBox();
|
||||
}
|
||||
|
||||
const auto date = unixtime(); // Incorrect, but ignored.
|
||||
if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
const auto date = base::unixtime::now(); // Incorrect, but ignored.
|
||||
if (_additional.isCreator(user) && user->isSelf()) {
|
||||
using Flag = MTPDchannelParticipantCreator::Flag;
|
||||
_additional.applyParticipant(MTP_channelParticipantCreator(
|
||||
MTP_flags(rank.isEmpty() ? Flag(0) : Flag::f_rank),
|
||||
MTP_int(user->bareId()),
|
||||
MTP_string(rank)));
|
||||
} else if (rights.c_chatAdminRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
MTP_int(date)));
|
||||
@@ -1450,16 +1499,19 @@ void ParticipantsBoxController::editAdminDone(
|
||||
removeRow(user);
|
||||
}
|
||||
} else {
|
||||
using Flag = MTPDchannelParticipantAdmin::Flag;
|
||||
const auto alreadyPromotedBy = _additional.adminPromotedBy(user);
|
||||
_additional.applyParticipant(MTP_channelParticipantAdmin(
|
||||
MTP_flags(MTPDchannelParticipantAdmin::Flag::f_can_edit),
|
||||
MTP_flags(Flag::f_can_edit
|
||||
| (rank.isEmpty() ? Flag(0) : Flag::f_rank)),
|
||||
MTP_int(user->bareId()),
|
||||
MTPint(), // inviter_id
|
||||
MTP_int(alreadyPromotedBy
|
||||
? alreadyPromotedBy->bareId()
|
||||
: user->session().userId()),
|
||||
MTP_int(date),
|
||||
rights));
|
||||
rights,
|
||||
MTP_string(rank)));
|
||||
if (_role == Role::Admins) {
|
||||
prependRow(user);
|
||||
} else if (_role == Role::Kicked || _role == Role::Restricted) {
|
||||
@@ -1509,7 +1561,7 @@ void ParticipantsBoxController::editRestrictedDone(
|
||||
_editParticipantBox->closeBox();
|
||||
}
|
||||
|
||||
const auto date = unixtime(); // Incorrect, but ignored.
|
||||
const auto date = base::unixtime::now(); // Incorrect, but ignored.
|
||||
if (rights.c_chatBannedRights().vflags().v == 0) {
|
||||
_additional.applyParticipant(MTP_channelParticipant(
|
||||
MTP_int(user->bareId()),
|
||||
@@ -1615,7 +1667,10 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
|
||||
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
SaveChatAdmin(chat, user, false, crl::guard(this, [=] {
|
||||
editAdminDone(user, MTP_chatAdminRights(MTP_flags(0)));
|
||||
editAdminDone(
|
||||
user,
|
||||
MTP_chatAdminRights(MTP_flags(0)),
|
||||
QString());
|
||||
}), nullptr);
|
||||
} else if (const auto channel = _peer->asChannel()) {
|
||||
const auto adminRights = _additional.adminRights(user);
|
||||
@@ -1623,7 +1678,10 @@ void ParticipantsBoxController::removeAdminSure(not_null<UserData*> user) {
|
||||
return;
|
||||
}
|
||||
RemoveAdmin(channel, user, *adminRights, crl::guard(this, [=] {
|
||||
editAdminDone(user, MTP_chatAdminRights(MTP_flags(0)));
|
||||
editAdminDone(
|
||||
user,
|
||||
MTP_chatAdminRights(MTP_flags(0)),
|
||||
QString());
|
||||
}), nullptr);
|
||||
}
|
||||
}
|
||||
@@ -1815,9 +1873,9 @@ void ParticipantsBoxController::subscribeToCreatorChange(
|
||||
MTP_int(Global::ChatSizeMax()),
|
||||
MTP_int(0)
|
||||
)).done([=](const MTPchannels_ChannelParticipants &result) {
|
||||
channel->mgInfo->creator = channel->amCreator()
|
||||
? channel->session().user().get()
|
||||
: nullptr;
|
||||
if (channel->amCreator()) {
|
||||
channel->mgInfo->creator = channel->session().user().get();
|
||||
}
|
||||
channel->mgInfo->lastAdmins.clear();
|
||||
channel->mgInfo->lastRestricted.clear();
|
||||
channel->mgInfo->lastParticipants.clear();
|
||||
|
||||
@@ -20,10 +20,13 @@ class SessionNavigation;
|
||||
|
||||
Fn<void(
|
||||
const MTPChatAdminRights &oldRights,
|
||||
const MTPChatAdminRights &newRights)> SaveAdminCallback(
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank)> SaveAdminCallback(
|
||||
not_null<PeerData*> peer,
|
||||
not_null<UserData*> user,
|
||||
Fn<void(const MTPChatAdminRights &newRights)> onDone,
|
||||
Fn<void(
|
||||
const MTPChatAdminRights &newRights,
|
||||
const QString &rank)> onDone,
|
||||
Fn<void()> onFail);
|
||||
|
||||
Fn<void(
|
||||
@@ -87,6 +90,7 @@ public:
|
||||
[[nodiscard]] bool canRestrictUser(not_null<UserData*> user) const;
|
||||
[[nodiscard]] std::optional<MTPChatAdminRights> adminRights(
|
||||
not_null<UserData*> user) const;
|
||||
QString adminRank(not_null<UserData*> user) const;
|
||||
[[nodiscard]] std::optional<MTPChatBannedRights> restrictedRights(
|
||||
not_null<UserData*> user) const;
|
||||
[[nodiscard]] bool isCreator(not_null<UserData*> user) const;
|
||||
@@ -104,7 +108,6 @@ private:
|
||||
UserData *applyBanned(const MTPDchannelParticipantBanned &data);
|
||||
void fillFromChat(not_null<ChatData*> chat);
|
||||
void fillFromChannel(not_null<ChannelData*> channel);
|
||||
void subscribeToCreatorChange(not_null<ChannelData*> channel);
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
Role _role = Role::Members;
|
||||
@@ -116,6 +119,7 @@ private:
|
||||
|
||||
// Data for channels.
|
||||
base::flat_map<not_null<UserData*>, MTPChatAdminRights> _adminRights;
|
||||
base::flat_map<not_null<UserData*>, QString> _adminRanks;
|
||||
base::flat_set<not_null<UserData*>> _adminCanEdit;
|
||||
base::flat_map<not_null<UserData*>, not_null<UserData*>> _adminPromotedBy;
|
||||
std::map<not_null<UserData*>, MTPChatBannedRights> _restrictedRights;
|
||||
@@ -145,6 +149,7 @@ public:
|
||||
not_null<PeerData*> peer,
|
||||
Role role);
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
void rowActionClicked(not_null<PeerListRow*> row) override;
|
||||
@@ -204,7 +209,8 @@ private:
|
||||
void showAdmin(not_null<UserData*> user);
|
||||
void editAdminDone(
|
||||
not_null<UserData*> user,
|
||||
const MTPChatAdminRights &rights);
|
||||
const MTPChatAdminRights &rights,
|
||||
const QString &rank);
|
||||
void showRestricted(not_null<UserData*> user);
|
||||
void editRestrictedDone(
|
||||
not_null<UserData*> user,
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_peer_info_box.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
@@ -130,21 +130,104 @@ void AddButtonDelete(
|
||||
nullptr));
|
||||
}
|
||||
|
||||
void SaveDefaultRestrictions(
|
||||
not_null<PeerData*> peer,
|
||||
MTPChatBannedRights rights,
|
||||
Fn<void()> done) {
|
||||
const auto api = &peer->session().api();
|
||||
const auto key = Api::RequestKey("default_restrictions", peer->id);
|
||||
|
||||
const auto requestId = api->request(
|
||||
MTPmessages_EditChatDefaultBannedRights(
|
||||
peer->input,
|
||||
rights)
|
||||
).done([=](const MTPUpdates &result) {
|
||||
api->clearModifyRequest(key);
|
||||
api->applyUpdates(result);
|
||||
done();
|
||||
}).fail([=](const RPCError &error) {
|
||||
api->clearModifyRequest(key);
|
||||
if (error.type() != qstr("CHAT_NOT_MODIFIED")) {
|
||||
return;
|
||||
}
|
||||
if (const auto chat = peer->asChat()) {
|
||||
chat->setDefaultRestrictions(rights);
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
channel->setDefaultRestrictions(rights);
|
||||
} else {
|
||||
Unexpected("Peer in ApiWrap::saveDefaultRestrictions.");
|
||||
}
|
||||
done();
|
||||
}).send();
|
||||
|
||||
api->registerModifyRequest(key, requestId);
|
||||
}
|
||||
|
||||
void SaveSlowmodeSeconds(
|
||||
not_null<ChannelData*> channel,
|
||||
int seconds,
|
||||
Fn<void()> done) {
|
||||
const auto api = &channel->session().api();
|
||||
const auto key = Api::RequestKey("slowmode_seconds", channel->id);
|
||||
|
||||
const auto requestId = api->request(MTPchannels_ToggleSlowMode(
|
||||
channel->inputChannel,
|
||||
MTP_int(seconds)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
api->clearModifyRequest(key);
|
||||
api->applyUpdates(result);
|
||||
channel->setSlowmodeSeconds(seconds);
|
||||
done();
|
||||
}).fail([=](const RPCError &error) {
|
||||
api->clearModifyRequest(key);
|
||||
if (error.type() != qstr("CHAT_NOT_MODIFIED")) {
|
||||
return;
|
||||
}
|
||||
channel->setSlowmodeSeconds(seconds);
|
||||
done();
|
||||
}).send();
|
||||
|
||||
api->registerModifyRequest(key, requestId);
|
||||
}
|
||||
|
||||
void ShowEditPermissions(not_null<PeerData*> peer) {
|
||||
const auto box = Ui::show(
|
||||
Box<EditPeerPermissionsBox>(peer),
|
||||
LayerOption::KeepOther);
|
||||
const auto saving = box->lifetime().make_state<int>(0);
|
||||
const auto save = [=](
|
||||
not_null<PeerData*> peer,
|
||||
EditPeerPermissionsBox::Result result) {
|
||||
Expects(result.slowmodeSeconds == 0 || peer->isChannel());
|
||||
|
||||
const auto close = crl::guard(box, [=] { box->closeBox(); });
|
||||
SaveDefaultRestrictions(
|
||||
peer,
|
||||
MTP_chatBannedRights(MTP_flags(result.rights), MTP_int(0)),
|
||||
close);
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
SaveSlowmodeSeconds(channel, result.slowmodeSeconds, close);
|
||||
}
|
||||
};
|
||||
box->saveEvents(
|
||||
) | rpl::start_with_next([=](MTPDchatBannedRights::Flags restrictions) {
|
||||
const auto callback = crl::guard(box, [=](bool success) {
|
||||
if (success) {
|
||||
box->closeBox();
|
||||
}
|
||||
) | rpl::start_with_next([=](EditPeerPermissionsBox::Result result) {
|
||||
if (*saving) {
|
||||
return;
|
||||
}
|
||||
*saving = true;
|
||||
|
||||
const auto saveFor = peer->migrateToOrMe();
|
||||
const auto chat = saveFor->asChat();
|
||||
if (!result.slowmodeSeconds || !chat) {
|
||||
save(saveFor, result);
|
||||
return;
|
||||
}
|
||||
const auto api = &peer->session().api();
|
||||
api->migrateChat(chat, [=](not_null<ChannelData*> channel) {
|
||||
save(channel, result);
|
||||
}, [=](const RPCError &error) {
|
||||
*saving = false;
|
||||
});
|
||||
peer->session().api().saveDefaultRestrictions(
|
||||
peer->migrateToOrMe(),
|
||||
MTP_chatBannedRights(MTP_flags(restrictions), MTP_int(0)),
|
||||
callback);
|
||||
}, box->lifetime());
|
||||
}
|
||||
|
||||
@@ -152,7 +235,7 @@ void ShowEditPermissions(not_null<PeerData*> peer) {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also add_contact_box.
|
||||
constexpr auto kMaxGroupChannelTitle = 128; // See also add_contact_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also add_contact_box.
|
||||
|
||||
class Controller
|
||||
@@ -160,6 +243,7 @@ class Controller
|
||||
, private MTP::Sender {
|
||||
public:
|
||||
Controller(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<BoxContent*> box,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
@@ -245,9 +329,10 @@ private:
|
||||
std::optional<QString> _usernameSavedValue;
|
||||
std::optional<bool> _signaturesSavedValue;
|
||||
|
||||
not_null<BoxContent*> _box;
|
||||
const not_null<Window::SessionNavigation*> _navigation;
|
||||
const not_null<BoxContent*> _box;
|
||||
not_null<PeerData*> _peer;
|
||||
bool _isGroup = false;
|
||||
const bool _isGroup = false;
|
||||
|
||||
base::unique_qptr<Ui::VerticalLayout> _wrap;
|
||||
Controls _controls;
|
||||
@@ -265,18 +350,20 @@ private:
|
||||
};
|
||||
|
||||
Controller::Controller(
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<BoxContent*> box,
|
||||
not_null<PeerData*> peer)
|
||||
: _box(box)
|
||||
: _navigation(navigation)
|
||||
, _box(box)
|
||||
, _peer(peer)
|
||||
, _isGroup(_peer->isChat() || _peer->isMegagroup()) {
|
||||
_box->setTitle(_isGroup
|
||||
? tr::lng_edit_group()
|
||||
: tr::lng_edit_channel_title());
|
||||
_box->addButton(tr::lng_settings_save(), [this] {
|
||||
_box->addButton(tr::lng_settings_save(), [=] {
|
||||
save();
|
||||
});
|
||||
_box->addButton(tr::lng_cancel(), [this] {
|
||||
_box->addButton(tr::lng_cancel(), [=] {
|
||||
_box->closeBox();
|
||||
});
|
||||
subscribeToMigration();
|
||||
@@ -377,10 +464,11 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
|
||||
result->entity()->setMaxLength(kMaxGroupChannelTitle);
|
||||
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
result->entity()->setInstantReplacesEnabled(
|
||||
Global::ReplaceEmojiValue());
|
||||
_peer->session().settings().replaceEmojiValue());
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
_wrap->window(),
|
||||
result->entity());
|
||||
result->entity(),
|
||||
&_peer->session());
|
||||
|
||||
QObject::connect(
|
||||
result->entity(),
|
||||
@@ -410,10 +498,11 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
|
||||
result->entity()->setMaxLength(kMaxChannelDescription);
|
||||
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
result->entity()->setInstantReplacesEnabled(
|
||||
Global::ReplaceEmojiValue());
|
||||
_peer->session().settings().replaceEmojiValue());
|
||||
Ui::Emoji::SuggestionsController::Init(
|
||||
_wrap->window(),
|
||||
result->entity());
|
||||
result->entity(),
|
||||
&_peer->session());
|
||||
|
||||
QObject::connect(
|
||||
result->entity(),
|
||||
@@ -538,7 +627,12 @@ void Controller::showEditLinkedChatBox() {
|
||||
|
||||
if (const auto chat = *_linkedChatSavedValue) {
|
||||
*box = Ui::show(
|
||||
EditLinkedChatBox(channel, chat, canEdit, callback),
|
||||
EditLinkedChatBox(
|
||||
_navigation,
|
||||
channel,
|
||||
chat,
|
||||
canEdit,
|
||||
callback),
|
||||
LayerOption::KeepOther);
|
||||
return;
|
||||
} else if (!canEdit || _linkedChatsRequestId) {
|
||||
@@ -561,7 +655,11 @@ void Controller::showEditLinkedChatBox() {
|
||||
chats.emplace_back(_peer->owner().processChat(item));
|
||||
}
|
||||
*box = Ui::show(
|
||||
EditLinkedChatBox(channel, std::move(chats), callback),
|
||||
EditLinkedChatBox(
|
||||
_navigation,
|
||||
channel,
|
||||
std::move(chats),
|
||||
callback),
|
||||
LayerOption::KeepOther);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_linkedChatsRequestId = 0;
|
||||
@@ -837,8 +935,12 @@ void Controller::fillManageSection() {
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_permissions(),
|
||||
Info::Profile::RestrictionsCountValue(_peer)
|
||||
| ToPositiveNumberStringRestrictions(),
|
||||
Info::Profile::MigratedOrMeValue(
|
||||
_peer
|
||||
) | rpl::map(
|
||||
Info::Profile::RestrictionsCountValue
|
||||
) | rpl::flatten_latest(
|
||||
) | ToPositiveNumberStringRestrictions(),
|
||||
[=] { ShowEditPermissions(_peer); },
|
||||
st::infoIconPermissions);
|
||||
}
|
||||
@@ -846,8 +948,12 @@ void Controller::fillManageSection() {
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_administrators(),
|
||||
Info::Profile::AdminsCountValue(_peer)
|
||||
| ToPositiveNumberString(),
|
||||
Info::Profile::MigratedOrMeValue(
|
||||
_peer
|
||||
) | rpl::map(
|
||||
Info::Profile::AdminsCountValue
|
||||
) | rpl::flatten_latest(
|
||||
) | ToPositiveNumberString(),
|
||||
[=] {
|
||||
ParticipantsBoxController::Start(
|
||||
navigation,
|
||||
@@ -860,8 +966,12 @@ void Controller::fillManageSection() {
|
||||
AddButtonWithCount(
|
||||
_controls.buttonsLayout,
|
||||
tr::lng_manage_peer_members(),
|
||||
Info::Profile::MembersCountValue(_peer)
|
||||
| ToPositiveNumberString(),
|
||||
Info::Profile::MigratedOrMeValue(
|
||||
_peer
|
||||
) | rpl::map(
|
||||
Info::Profile::MembersCountValue
|
||||
) | rpl::flatten_latest(
|
||||
) | ToPositiveNumberString(),
|
||||
[=] {
|
||||
ParticipantsBoxController::Start(
|
||||
navigation,
|
||||
@@ -1343,12 +1453,18 @@ void Controller::deleteChannel() {
|
||||
|
||||
EditPeerInfoBox::EditPeerInfoBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer)
|
||||
: _peer(peer->migrateToOrMe()) {
|
||||
: _navigation(navigation)
|
||||
, _peer(peer->migrateToOrMe()) {
|
||||
}
|
||||
|
||||
void EditPeerInfoBox::prepare() {
|
||||
const auto controller = Ui::CreateChild<Controller>(this, this, _peer);
|
||||
const auto controller = Ui::CreateChild<Controller>(
|
||||
this,
|
||||
_navigation,
|
||||
this,
|
||||
_peer);
|
||||
_focusRequests.events(
|
||||
) | rpl::start_with_next(
|
||||
[=] { controller->setFocus(); },
|
||||
|
||||
@@ -14,6 +14,10 @@ namespace style {
|
||||
struct InfoProfileCountButton;
|
||||
} // namespace style
|
||||
|
||||
namespace Window {
|
||||
class SessionNavigation;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
@@ -26,7 +30,10 @@ class Button;
|
||||
|
||||
class EditPeerInfoBox : public BoxContent {
|
||||
public:
|
||||
EditPeerInfoBox(QWidget*, not_null<PeerData*> peer);
|
||||
EditPeerInfoBox(
|
||||
QWidget*,
|
||||
not_null<Window::SessionNavigation*> navigation,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
void setInnerFocus() override {
|
||||
_focusRequests.fire({});
|
||||
@@ -47,6 +54,7 @@ protected:
|
||||
|
||||
private:
|
||||
rpl::event_stream<> _focusRequests;
|
||||
not_null<Window::SessionNavigation*> _navigation;
|
||||
not_null<PeerData*> _peer;
|
||||
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "info/profile/info_profile_button.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
@@ -26,6 +27,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSlowmodeValues = 7;
|
||||
|
||||
int SlowmodeDelayByIndex(int index) {
|
||||
Expects(index >= 0 && index < kSlowmodeValues);
|
||||
|
||||
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;
|
||||
}
|
||||
Unexpected("Index in SlowmodeDelayByIndex.");
|
||||
}
|
||||
|
||||
template <typename CheckboxesMap, typename DependenciesMap>
|
||||
void ApplyDependencies(
|
||||
const CheckboxesMap &checkboxes,
|
||||
@@ -290,8 +308,7 @@ EditPeerPermissionsBox::EditPeerPermissionsBox(
|
||||
: _peer(peer->migrateToOrMe()) {
|
||||
}
|
||||
|
||||
auto EditPeerPermissionsBox::saveEvents() const
|
||||
-> rpl::producer<MTPDchatBannedRights::Flags> {
|
||||
auto EditPeerPermissionsBox::saveEvents() const -> rpl::producer<Result> {
|
||||
Expects(_save != nullptr);
|
||||
|
||||
return _save->clicks() | rpl::map(_value);
|
||||
@@ -342,15 +359,158 @@ void EditPeerPermissionsBox::prepare() {
|
||||
|
||||
inner->add(std::move(checkboxes));
|
||||
|
||||
const auto getSlowmodeSeconds = addSlowmodeSlider(inner);
|
||||
addBannedButtons(inner);
|
||||
|
||||
_value = getRestrictions;
|
||||
_value = [=, rights = getRestrictions]() -> Result {
|
||||
return { rights(), getSlowmodeSeconds() };
|
||||
};
|
||||
_save = addButton(tr::lng_settings_save());
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
setDimensionsToContent(st::boxWidth, inner);
|
||||
}
|
||||
|
||||
Fn<int()> EditPeerPermissionsBox::addSlowmodeSlider(
|
||||
not_null<Ui::VerticalLayout*> container) {
|
||||
using namespace rpl::mappers;
|
||||
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
if (!chat->amCreator()) {
|
||||
return [] { return 0; };
|
||||
}
|
||||
}
|
||||
const auto channel = _peer->asChannel();
|
||||
auto &lifetime = container->lifetime();
|
||||
const auto secondsCount = lifetime.make_state<rpl::variable<int>>(
|
||||
channel ? channel->slowmodeSeconds() : 0);
|
||||
|
||||
container->add(
|
||||
object_ptr<BoxContentDivider>(container),
|
||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||
|
||||
container->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
tr::lng_rights_slowmode_header(),
|
||||
st::rightsHeaderLabel),
|
||||
st::rightsHeaderMargin);
|
||||
|
||||
addSlowmodeLabels(container);
|
||||
|
||||
const auto slider = container->add(
|
||||
object_ptr<Ui::MediaSlider>(container, st::localStorageLimitSlider),
|
||||
st::localStorageLimitMargin);
|
||||
slider->resize(st::localStorageLimitSlider.seekSize);
|
||||
slider->setPseudoDiscrete(
|
||||
kSlowmodeValues,
|
||||
SlowmodeDelayByIndex,
|
||||
secondsCount->current(),
|
||||
[=](int seconds) {
|
||||
(*secondsCount) = seconds;
|
||||
});
|
||||
|
||||
auto hasSlowMode = secondsCount->value(
|
||||
) | rpl::map(
|
||||
_1 != 0
|
||||
) | rpl::distinct_until_changed();
|
||||
|
||||
auto useSeconds = secondsCount->value(
|
||||
) | rpl::map(
|
||||
_1 < 60
|
||||
) | rpl::distinct_until_changed();
|
||||
|
||||
auto interval = rpl::combine(
|
||||
std::move(useSeconds),
|
||||
tr::lng_rights_slowmode_interval_seconds(
|
||||
lt_count,
|
||||
secondsCount->value() | tr::to_count()),
|
||||
tr::lng_rights_slowmode_interval_minutes(
|
||||
lt_count,
|
||||
secondsCount->value() | rpl::map(_1 / 60.))
|
||||
) | rpl::map([](
|
||||
bool use,
|
||||
const QString &seconds,
|
||||
const QString &minutes) {
|
||||
return use ? seconds : minutes;
|
||||
});
|
||||
|
||||
auto aboutText = rpl::combine(
|
||||
std::move(hasSlowMode),
|
||||
tr::lng_rights_slowmode_about(),
|
||||
tr::lng_rights_slowmode_about_interval(
|
||||
lt_interval,
|
||||
std::move(interval))
|
||||
) | rpl::map([](
|
||||
bool has,
|
||||
const QString &about,
|
||||
const QString &aboutInterval) {
|
||||
return has ? aboutInterval : about;
|
||||
});
|
||||
|
||||
const auto about = container->add(
|
||||
object_ptr<Ui::DividerLabel>(
|
||||
container,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
container,
|
||||
std::move(aboutText),
|
||||
st::boxDividerLabel),
|
||||
st::proxyAboutPadding),
|
||||
style::margins(0, st::infoProfileSkip, 0, st::infoProfileSkip));
|
||||
|
||||
return [=] { return secondsCount->current(); };
|
||||
}
|
||||
|
||||
void EditPeerPermissionsBox::addSlowmodeLabels(
|
||||
not_null<Ui::VerticalLayout*> container) {
|
||||
const auto labels = container->add(
|
||||
object_ptr<Ui::FixedHeightWidget>(container, st::normalFont->height),
|
||||
st::slowmodeLabelsMargin);
|
||||
for (auto i = 0; i != kSlowmodeValues; ++i) {
|
||||
const auto seconds = SlowmodeDelayByIndex(i);
|
||||
const auto label = Ui::CreateChild<Ui::LabelSimple>(
|
||||
labels,
|
||||
st::slowmodeLabel,
|
||||
(!seconds
|
||||
? tr::lng_rights_slowmode_off(tr::now)
|
||||
: (seconds < 60)
|
||||
? tr::lng_rights_slowmode_seconds(
|
||||
tr::now,
|
||||
lt_count,
|
||||
seconds)
|
||||
: (seconds < 3600)
|
||||
? tr::lng_rights_slowmode_minutes(
|
||||
tr::now,
|
||||
lt_count,
|
||||
seconds / 60)
|
||||
: tr::lng_rights_slowmode_hours(
|
||||
tr::now,
|
||||
lt_count,
|
||||
seconds / 3600)));
|
||||
rpl::combine(
|
||||
labels->widthValue(),
|
||||
label->widthValue()
|
||||
) | rpl::start_with_next([=](int outer, int inner) {
|
||||
const auto skip = st::localStorageLimitMargin;
|
||||
const auto size = st::localStorageLimitSlider.seekSize;
|
||||
const auto available = outer
|
||||
- skip.left()
|
||||
- skip.right()
|
||||
- size.width();
|
||||
const auto shift = (i == 0)
|
||||
? -(size.width() / 2)
|
||||
: (i + 1 == kSlowmodeValues)
|
||||
? (size.width() - (size.width() / 2) - inner)
|
||||
: (-inner / 2);
|
||||
const auto left = skip.left()
|
||||
+ (size.width() / 2)
|
||||
+ (i * available) / (kSlowmodeValues - 1)
|
||||
+ shift;
|
||||
label->moveToLeft(left, 0, outer);
|
||||
}, label->lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
void EditPeerPermissionsBox::addBannedButtons(
|
||||
not_null<Ui::VerticalLayout*> container) {
|
||||
if (const auto chat = _peer->asChat()) {
|
||||
@@ -360,10 +520,6 @@ void EditPeerPermissionsBox::addBannedButtons(
|
||||
}
|
||||
const auto channel = _peer->asChannel();
|
||||
|
||||
container->add(
|
||||
object_ptr<BoxContentDivider>(container),
|
||||
{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
|
||||
|
||||
const auto navigation = App::wnd()->sessionController();
|
||||
container->add(EditPeerInfoBox::CreateButton(
|
||||
container,
|
||||
|
||||
@@ -19,17 +19,24 @@ class EditPeerPermissionsBox : public BoxContent {
|
||||
public:
|
||||
EditPeerPermissionsBox(QWidget*, not_null<PeerData*> peer);
|
||||
|
||||
rpl::producer<MTPDchatBannedRights::Flags> saveEvents() const;
|
||||
struct Result {
|
||||
MTPDchatBannedRights::Flags rights;
|
||||
int slowmodeSeconds = 0;
|
||||
};
|
||||
|
||||
rpl::producer<Result> saveEvents() const;
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
|
||||
private:
|
||||
Fn<int()> addSlowmodeSlider(not_null<Ui::VerticalLayout*> container);
|
||||
void addSlowmodeLabels(not_null<Ui::VerticalLayout*> container);
|
||||
void addBannedButtons(not_null<Ui::VerticalLayout*> container);
|
||||
|
||||
not_null<PeerData*> _peer;
|
||||
Ui::RoundButton *_save = nullptr;
|
||||
Fn<MTPDchatBannedRights::Flags()> _value;
|
||||
Fn<Result()> _value;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/peers/edit_peer_type_box.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
@@ -39,6 +39,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include <rpl/flatten_latest.h>
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QClipboard>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kUsernameCheckTimeout = crl::time(200);
|
||||
@@ -464,7 +467,9 @@ void Controller::askUsernameRevoke() {
|
||||
checkUsernameAvailability();
|
||||
});
|
||||
Ui::show(
|
||||
Box<RevokePublicLinkBox>(std::move(revokeCallback)),
|
||||
Box<RevokePublicLinkBox>(
|
||||
&_peer->session(),
|
||||
std::move(revokeCallback)),
|
||||
LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
@@ -605,7 +610,7 @@ object_ptr<Ui::RpWidget> Controller::createInviteLinkEdit() {
|
||||
_controls.inviteLink->setContextCopyText(QString());
|
||||
_controls.inviteLink->setBreakEverywhere(true);
|
||||
_controls.inviteLink->setClickHandlerFilter([=](auto&&...) {
|
||||
QApplication::clipboard()->setText(inviteLinkText());
|
||||
QGuiApplication::clipboard()->setText(inviteLinkText());
|
||||
Ui::Toast::Show(tr::lng_group_invite_copied(tr::now));
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace {
|
||||
@@ -25,8 +25,13 @@ constexpr auto kRateCallCommentLengthMax = 200;
|
||||
|
||||
} // namespace
|
||||
|
||||
RateCallBox::RateCallBox(QWidget*, uint64 callId, uint64 callAccessHash)
|
||||
: _callId(callId)
|
||||
RateCallBox::RateCallBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
uint64 callId,
|
||||
uint64 callAccessHash)
|
||||
: _session(session)
|
||||
, _callId(callId)
|
||||
, _callAccessHash(callAccessHash) {
|
||||
}
|
||||
|
||||
@@ -35,7 +40,7 @@ void RateCallBox::prepare() {
|
||||
addButton(tr::lng_cancel(), [this] { closeBox(); });
|
||||
|
||||
for (auto i = 0; i < kMaxRating; ++i) {
|
||||
_stars.push_back(object_ptr<Ui::IconButton>(this, st::callRatingStar));
|
||||
_stars.emplace_back(this, st::callRatingStar);
|
||||
_stars.back()->setClickedCallback([this, value = i + 1] { ratingChanged(value); });
|
||||
_stars.back()->show();
|
||||
}
|
||||
@@ -121,7 +126,7 @@ void RateCallBox::send() {
|
||||
MTP_int(_rating),
|
||||
MTP_string(comment)
|
||||
)).done([=](const MTPUpdates &updates) {
|
||||
Auth().api().applyUpdates(updates);
|
||||
_session->api().applyUpdates(updates);
|
||||
closeBox();
|
||||
}).fail([=](const RPCError &error) { closeBox(); }).send();
|
||||
}
|
||||
|
||||
@@ -16,9 +16,17 @@ class FlatLabel;
|
||||
class IconButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
class RateCallBox : public BoxContent, private MTP::Sender {
|
||||
public:
|
||||
RateCallBox(QWidget*, uint64 callId, uint64 callAccessHash);
|
||||
RateCallBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
uint64 callId,
|
||||
uint64 callAccessHash);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -32,6 +40,8 @@ private:
|
||||
void send();
|
||||
void commentResized();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
|
||||
uint64 _callId = 0;
|
||||
uint64 _callAccessHash = 0;
|
||||
int _rating = 0;
|
||||
|
||||
@@ -11,13 +11,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
SelfDestructionBox::SelfDestructionBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<int> preloaded)
|
||||
: _ttlValues{ 30, 90, 180, 365 }
|
||||
: _session(session)
|
||||
, _ttlValues{ 30, 90, 180, 365 }
|
||||
, _loading(
|
||||
this,
|
||||
tr::lng_contacts_loading(tr::now),
|
||||
@@ -73,7 +75,7 @@ void SelfDestructionBox::showContent() {
|
||||
|
||||
clearButtons();
|
||||
addButton(tr::lng_settings_save(), [=] {
|
||||
Auth().api().saveSelfDestruct(_ttlGroup->value());
|
||||
_session->api().saveSelfDestruct(_ttlGroup->value());
|
||||
closeBox();
|
||||
});
|
||||
addButton(tr::lng_cancel(), [=] { closeBox(); });
|
||||
|
||||
@@ -16,9 +16,16 @@ class Radiobutton;
|
||||
class FlatLabel;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
class SelfDestructionBox : public BoxContent, private MTP::Sender {
|
||||
public:
|
||||
SelfDestructionBox(QWidget*, rpl::producer<int> preloaded);
|
||||
SelfDestructionBox(
|
||||
QWidget*,
|
||||
not_null<Main::Session*> session,
|
||||
rpl::producer<int> preloaded);
|
||||
|
||||
static QString DaysLabel(int days);
|
||||
|
||||
@@ -29,6 +36,7 @@ private:
|
||||
void gotCurrent(int days);
|
||||
void showContent();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
bool _prepared = false;
|
||||
std::vector<int> _ttlValues;
|
||||
object_ptr<Ui::FlatLabel> _description = { nullptr };
|
||||
|
||||