Compare commits
191 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ab40de8b9 | ||
|
|
35659536c5 | ||
|
|
31ec831c71 | ||
|
|
866c5e9b7b | ||
|
|
d6a00523a8 | ||
|
|
9ebeddbed8 | ||
|
|
c85fd368fe | ||
|
|
16f3ca87f5 | ||
|
|
e47d110f28 | ||
|
|
20b67ee000 | ||
|
|
d8a4ede4b5 | ||
|
|
5ed15cd0b3 | ||
|
|
87c07a13a1 | ||
|
|
73ce722147 | ||
|
|
233be5ec13 | ||
|
|
50b78034a5 | ||
|
|
777bf7d8d9 | ||
|
|
4a4544c883 | ||
|
|
d03d699331 | ||
|
|
d16cbbf279 | ||
|
|
72f95b984f | ||
|
|
9055b33e92 | ||
|
|
812ffb4297 | ||
|
|
958d55a594 | ||
|
|
4559888113 | ||
|
|
294ed1bb17 | ||
|
|
a52392241d | ||
|
|
31998406dd | ||
|
|
5a63fc7bbb | ||
|
|
a2d4b9260e | ||
|
|
1d1bd5e1e7 | ||
|
|
a42fb1f7b7 | ||
|
|
dd81f5d59f | ||
|
|
8d1cdea31a | ||
|
|
fae4b92d8d | ||
|
|
18f38f0983 | ||
|
|
a29e8f9a06 | ||
|
|
5c0cc8a947 | ||
|
|
308fb19da4 | ||
|
|
6aecb81c23 | ||
|
|
72b29dd90d | ||
|
|
2f5f1fbac9 | ||
|
|
ab797b4dff | ||
|
|
ab5f35e952 | ||
|
|
275ec3e679 | ||
|
|
44b551898d | ||
|
|
4a8b6ff5ba | ||
|
|
60582a24ab | ||
|
|
67e698a374 | ||
|
|
46af87a00a | ||
|
|
d6d942bc64 | ||
|
|
5cb44834dc | ||
|
|
6795ecea61 | ||
|
|
22bdf15825 | ||
|
|
704e3c9423 | ||
|
|
67ea175fc6 | ||
|
|
6c2a39f1fc | ||
|
|
395f22063b | ||
|
|
9f6130cd20 | ||
|
|
5b615519e8 | ||
|
|
03b7a3ca2b | ||
|
|
4f1a633019 | ||
|
|
362b3bc578 | ||
|
|
6de3112c8a | ||
|
|
1064208be9 | ||
|
|
49578836be | ||
|
|
e4ae5bfcad | ||
|
|
e82430cb50 | ||
|
|
e7ce4ca10a | ||
|
|
f8b2e474b9 | ||
|
|
8969a7d929 | ||
|
|
ead31757d7 | ||
|
|
c20cf243db | ||
|
|
9142313a6b | ||
|
|
ccb57a6d69 | ||
|
|
11fd757e99 | ||
|
|
1c48f33dc1 | ||
|
|
9903546a2d | ||
|
|
e4e05a14b7 | ||
|
|
62389f5ef7 | ||
|
|
4e2a109a46 | ||
|
|
35dcbe0aa0 | ||
|
|
d0e854e9d8 | ||
|
|
f1519b76f6 | ||
|
|
94bfd59b76 | ||
|
|
2790919733 | ||
|
|
f0b7ff24b1 | ||
|
|
5cfead762d | ||
|
|
a2dabfde56 | ||
|
|
b0a9d26a94 | ||
|
|
2bc60fa54f | ||
|
|
c2aa9c571c | ||
|
|
083b520eee | ||
|
|
1392e05ab1 | ||
|
|
b2014f403e | ||
|
|
f633ead3ab | ||
|
|
07e8a2bd85 | ||
|
|
ddb4527159 | ||
|
|
e122353bfb | ||
|
|
b4a2e84aa3 | ||
|
|
b64bbc7708 | ||
|
|
364aaaae5b | ||
|
|
2bd8737410 | ||
|
|
e62e7d1de2 | ||
|
|
bffd852b4e | ||
|
|
fdfdd0acce | ||
|
|
80be464d95 | ||
|
|
2878e46d2b | ||
|
|
b198c9b975 | ||
|
|
cf7a779689 | ||
|
|
53b3b24867 | ||
|
|
8edf4c8711 | ||
|
|
ad12d6cc46 | ||
|
|
7db7c05da8 | ||
|
|
43d19920e0 | ||
|
|
bfc748cd31 | ||
|
|
b3059248d4 | ||
|
|
0cbad9098e | ||
|
|
5c5438c12e | ||
|
|
734c410879 | ||
|
|
4b4e22d59d | ||
|
|
8f63fa71c9 | ||
|
|
267293d21b | ||
|
|
20ee1fa0d3 | ||
|
|
c70e9b529a | ||
|
|
e42e973ed5 | ||
|
|
03037121aa | ||
|
|
d257b2ee17 | ||
|
|
59a1e13955 | ||
|
|
4e858ba839 | ||
|
|
0eb3d20250 | ||
|
|
c0896f5357 | ||
|
|
f334e2d0f4 | ||
|
|
dfbe11efdb | ||
|
|
becbad32c9 | ||
|
|
46976c4e03 | ||
|
|
1e4cf4c466 | ||
|
|
2e1517474d | ||
|
|
08897aa83a | ||
|
|
7252e9b266 | ||
|
|
07d8dafa5e | ||
|
|
cca46448fe | ||
|
|
3f7947b404 | ||
|
|
4870558827 | ||
|
|
c23ec41622 | ||
|
|
544aef19b4 | ||
|
|
24834ced9e | ||
|
|
10fa6f0c13 | ||
|
|
7a32ad5409 | ||
|
|
4f7df6987c | ||
|
|
3d75d21a3e | ||
|
|
250718e766 | ||
|
|
a5cbade8ec | ||
|
|
3ac50cf77f | ||
|
|
f35bf41d26 | ||
|
|
f1816815a9 | ||
|
|
37bf9ffcff | ||
|
|
5e7642b42a | ||
|
|
6f6ec217e3 | ||
|
|
017ec87d60 | ||
|
|
30dd8fe070 | ||
|
|
8e442563f2 | ||
|
|
c43dcf0567 | ||
|
|
f2a5a29d12 | ||
|
|
678b9a8eb5 | ||
|
|
96f7c0c02e | ||
|
|
66b7b6da2a | ||
|
|
38daffdbfe | ||
|
|
0238c03956 | ||
|
|
425e56b3ea | ||
|
|
4478c0a143 | ||
|
|
a053384618 | ||
|
|
694e8cd19f | ||
|
|
4bcd1e3c59 | ||
|
|
5f063c0151 | ||
|
|
5a1d4d55c6 | ||
|
|
d3f85b4c4e | ||
|
|
df9ec4b466 | ||
|
|
4f9507ed97 | ||
|
|
f761b6aa9e | ||
|
|
168a7ce2e5 | ||
|
|
4b763a76df | ||
|
|
8764da787b | ||
|
|
7d8ba15252 | ||
|
|
96c0c30f7c | ||
|
|
bb6ab5314c | ||
|
|
e3c6abfc3d | ||
|
|
5c5bccae0b | ||
|
|
296e009808 | ||
|
|
4d84781a65 | ||
|
|
710b9bf454 |
@@ -585,10 +585,10 @@ buildCustomQt() {
|
||||
info_msg "Downloading and building patched qt"
|
||||
|
||||
if [ -d "$EXTERNAL/qt${QT_VERSION}" ]; then
|
||||
rm -rf "$EXTERNAL/qt${QT_VERSION}"
|
||||
sudo rm -rf "$EXTERNAL/qt${QT_VERSION}"
|
||||
fi
|
||||
cd $QT_PATH
|
||||
rm -rf *
|
||||
sudo rm -rf *
|
||||
|
||||
cd "$EXTERNAL"
|
||||
git clone git://code.qt.io/qt/qt5.git qt${QT_VERSION}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/configure b/configure
|
||||
index cb8d78fd3c..cadb3f0a88 100755
|
||||
index cb8d78fd3cb..cadb3f0a880 100755
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -511,7 +511,8 @@ if [ "$BUILD_ON_MAC" = "yes" ]; then
|
||||
@@ -13,7 +13,7 @@ index cb8d78fd3c..cadb3f0a88 100755
|
||||
echo " Xcode not set up properly. You may need to confirm the license" >&2
|
||||
echo " agreement by running /usr/bin/xcodebuild without arguments." >&2
|
||||
diff --git a/mkspecs/common/g++-macx.conf b/mkspecs/common/g++-macx.conf
|
||||
index 086510dd96..c485967863 100644
|
||||
index 086510dd963..c485967863d 100644
|
||||
--- a/mkspecs/common/g++-macx.conf
|
||||
+++ b/mkspecs/common/g++-macx.conf
|
||||
@@ -14,7 +14,13 @@ QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -gdwarf-2
|
||||
@@ -32,7 +32,7 @@ index 086510dd96..c485967863 100644
|
||||
QMAKE_XCODE_GCC_VERSION = com.apple.compilers.llvmgcc42
|
||||
|
||||
diff --git a/mkspecs/features/mac/default_pre.prf b/mkspecs/features/mac/default_pre.prf
|
||||
index 0cc8cd6dfd..ca9725b779 100644
|
||||
index 0cc8cd6dfdd..ca9725b7791 100644
|
||||
--- a/mkspecs/features/mac/default_pre.prf
|
||||
+++ b/mkspecs/features/mac/default_pre.prf
|
||||
@@ -12,7 +12,9 @@ isEmpty(QMAKE_XCODE_DEVELOPER_PATH) {
|
||||
@@ -47,7 +47,7 @@ index 0cc8cd6dfd..ca9725b779 100644
|
||||
}
|
||||
|
||||
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
|
||||
index bb79a139b3..5d595bc3b3 100644
|
||||
index bb79a139b3c..5d595bc3b34 100644
|
||||
--- a/src/gui/image/qbmphandler.cpp
|
||||
+++ b/src/gui/image/qbmphandler.cpp
|
||||
@@ -220,6 +220,10 @@ static bool read_dib_body(QDataStream &s, const BMP_INFOHDR &bi, int offset, int
|
||||
@@ -74,7 +74,7 @@ index bb79a139b3..5d595bc3b3 100644
|
||||
if (ncols > 0) { // read color table
|
||||
uchar rgb[4];
|
||||
diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h
|
||||
index ebff9509ab..4300ca4c0f 100644
|
||||
index ebff9509ab2..4300ca4c0f0 100644
|
||||
--- a/src/gui/painting/qpaintengine_p.h
|
||||
+++ b/src/gui/painting/qpaintengine_p.h
|
||||
@@ -87,8 +87,18 @@ public:
|
||||
@@ -98,7 +98,7 @@ index ebff9509ab..4300ca4c0f 100644
|
||||
|
||||
// Make sure we're inside the viewport.
|
||||
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
|
||||
index 4879ae51d7..56cdcbaf01 100644
|
||||
index 4879ae51d7d..56cdcbaf01c 100644
|
||||
--- a/src/gui/text/qtextlayout.cpp
|
||||
+++ b/src/gui/text/qtextlayout.cpp
|
||||
@@ -654,6 +654,9 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const
|
||||
@@ -175,7 +175,7 @@ index 4879ae51d7..56cdcbaf01 100644
|
||||
|
||||
inline void resetRightBearing()
|
||||
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
|
||||
index cbe42c3844..b273db7e78 100644
|
||||
index cbe42c38444..b273db7e78c 100644
|
||||
--- a/src/gui/text/qtextlayout.h
|
||||
+++ b/src/gui/text/qtextlayout.h
|
||||
@@ -194,6 +194,9 @@ private:
|
||||
@@ -188,8 +188,21 @@ index cbe42c3844..b273db7e78 100644
|
||||
};
|
||||
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
|
||||
index 360f9722c70..f28f289ef6a 100644
|
||||
--- a/src/network/access/qhttpnetworkconnection.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnection.cpp
|
||||
@@ -118,6 +118,8 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
|
||||
{
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].socket) {
|
||||
+ // Patch: backport critical bugfix from '4f959b6b30' commit.
|
||||
+ QObject::disconnect(channels[i].socket, Q_NULLPTR, &channels[i], Q_NULLPTR);
|
||||
channels[i].socket->close();
|
||||
delete channels[i].socket;
|
||||
}
|
||||
diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
|
||||
index ca7afb7d1b..25ae50008d 100644
|
||||
index ca7afb7d1b9..25ae50008db 100644
|
||||
--- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
|
||||
+++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm
|
||||
@@ -256,6 +256,13 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd)
|
||||
@@ -206,8 +219,22 @@ index ca7afb7d1b..25ae50008d 100644
|
||||
fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute);
|
||||
fd->weight = QFont::Normal;
|
||||
fd->style = QFont::StyleNormal;
|
||||
diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
index 6e2c8a2a9af..3cace8abcbc 100644
|
||||
--- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
+++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
@@ -717,7 +717,8 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t, QPainterPath *, glyph_metric
|
||||
|
||||
QFixed QCoreTextFontEngine::emSquareSize() const
|
||||
{
|
||||
- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont)));
|
||||
+ // Patch: Fix build for Xcode 9.3.1.
|
||||
+ return QFixed(int(CTFontGetUnitsPerEm(ctfont)));
|
||||
}
|
||||
|
||||
QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||
index 92358ecc74..694fee7350 100644
|
||||
index 92358ecc745..694fee73507 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
|
||||
@@ -213,7 +213,8 @@ static void cleanupCocoaApplicationDelegate()
|
||||
@@ -244,7 +271,7 @@ index 92358ecc74..694fee7350 100644
|
||||
|
||||
- (void)appleEventQuit:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoacursor.mm b/src/plugins/platforms/cocoa/qcocoacursor.mm
|
||||
index b81b9a0b1c..4e59e833b1 100644
|
||||
index b81b9a0b1c2..4e59e833b1d 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoacursor.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoacursor.mm
|
||||
@@ -81,7 +81,7 @@ void QCocoaCursor::setPos(const QPoint &position)
|
||||
@@ -257,7 +284,7 @@ index b81b9a0b1c..4e59e833b1 100644
|
||||
CFRelease(e);
|
||||
}
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
index 9850f83dea..218ed7e81c 100644
|
||||
index 9850f83dea8..b2e1d3dfda7 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
@@ -649,9 +649,10 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm
|
||||
@@ -290,7 +317,7 @@ index 9850f83dea..218ed7e81c 100644
|
||||
}
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
|
||||
index 9fd05a65ee..dea60720e7 100644
|
||||
index 9fd05a65ee9..dea60720e78 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
|
||||
@@ -402,14 +402,24 @@ void QCocoaIntegration::updateScreens()
|
||||
@@ -321,7 +348,7 @@ index 9fd05a65ee..dea60720e7 100644
|
||||
|
||||
QCocoaScreen *QCocoaIntegration::screenAtIndex(int index)
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
index e46eaff6be..c62db534a2 100644
|
||||
index e46eaff6be3..c62db534a2d 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
@@ -382,6 +382,12 @@ bool QCocoaKeyMapper::updateKeyboard()
|
||||
@@ -348,7 +375,7 @@ index e46eaff6be..c62db534a2 100644
|
||||
}
|
||||
return ret;
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index 83c960d931..03ae9696af 100755
|
||||
index 83c960d9317..03ae9696afe 100755
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -102,7 +102,10 @@ QT_USE_NAMESPACE
|
||||
@@ -516,7 +543,7 @@ index 83c960d931..03ae9696af 100755
|
||||
}
|
||||
@end
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
index 4d0458a4aa..3357a5ef81 100644
|
||||
index 4d0458a4aa2..3357a5ef817 100644
|
||||
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
@@ -167,7 +167,8 @@ static bool isMouseEvent(NSEvent *ev)
|
||||
@@ -579,7 +606,7 @@ index 4d0458a4aa..3357a5ef81 100644
|
||||
[iconButton setImage:image];
|
||||
[image release];
|
||||
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
|
||||
index a18ee7ff71..1f91feb0ae 100644
|
||||
index a18ee7ff71d..1f91feb0ae8 100644
|
||||
--- a/src/plugins/platforms/cocoa/qnsview.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qnsview.mm
|
||||
@@ -393,7 +393,9 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil;
|
||||
@@ -648,7 +675,7 @@ index a18ee7ff71..1f91feb0ae 100644
|
||||
}
|
||||
return [super performKeyEquivalent:nsevent];
|
||||
diff --git a/src/tools/qlalr/lalr.cpp b/src/tools/qlalr/lalr.cpp
|
||||
index c68076477f..e2a7aafa58 100644
|
||||
index c68076477f3..e2a7aafa586 100644
|
||||
--- a/src/tools/qlalr/lalr.cpp
|
||||
+++ b/src/tools/qlalr/lalr.cpp
|
||||
@@ -246,11 +246,13 @@ void Grammar::buildExtendedGrammar ()
|
||||
@@ -688,7 +715,7 @@ index c68076477f..e2a7aafa58 100644
|
||||
continue;
|
||||
|
||||
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
|
||||
index 7396808442..7178aecf80 100644
|
||||
index 7396808442e..7178aecf800 100644
|
||||
--- a/src/widgets/kernel/qwidget.cpp
|
||||
+++ b/src/widgets/kernel/qwidget.cpp
|
||||
@@ -4722,6 +4722,17 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
|
||||
@@ -741,7 +768,7 @@ index 7396808442..7178aecf80 100644
|
||||
|| (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier)))
|
||||
res = focusNextPrevChild(false);
|
||||
diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm
|
||||
index 0845a5eb02..5735cb6b39 100644
|
||||
index 0845a5eb02f..5735cb6b396 100644
|
||||
--- a/src/widgets/styles/qmacstyle_mac.mm
|
||||
+++ b/src/widgets/styles/qmacstyle_mac.mm
|
||||
@@ -3667,9 +3667,11 @@ void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter
|
||||
@@ -759,7 +786,7 @@ index 0845a5eb02..5735cb6b39 100644
|
||||
}
|
||||
|
||||
diff --git a/src/widgets/util/qsystemtrayicon_qpa.cpp b/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||
index f98aeaf678..00c0734129 100644
|
||||
index f98aeaf6782..00c0734129e 100644
|
||||
--- a/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||
+++ b/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||
@@ -99,13 +99,18 @@ void QSystemTrayIconPrivate::updateIcon_sys()
|
||||
@@ -788,7 +815,7 @@ index f98aeaf678..00c0734129 100644
|
||||
}
|
||||
|
||||
diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
index 75f30599be..980f2be1e9 100644
|
||||
index 75f30599be4..980f2be1e93 100644
|
||||
--- a/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
+++ b/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
@@ -1867,7 +1867,8 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event)
|
||||
@@ -802,7 +829,7 @@ index 75f30599be..980f2be1e9 100644
|
||||
#ifndef QT_NO_COMPLETER
|
||||
complete(event->key());
|
||||
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
index 96438a0bdf..b0b7206405 100644
|
||||
index 96438a0bdf7..b0b72064056 100644
|
||||
--- a/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
+++ b/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
@@ -1342,7 +1342,8 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e)
|
||||
|
||||
@@ -221,6 +221,19 @@ index f74d4d4229..8ad672c9fe 100644
|
||||
};
|
||||
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
|
||||
index c4cb8e65c0..45793e364f 100644
|
||||
--- a/src/network/access/qhttpnetworkconnection.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnection.cpp
|
||||
@@ -110,6 +110,8 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
|
||||
{
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].socket) {
|
||||
+ // Patch: backport critical bugfix from '4f959b6b30' commit.
|
||||
+ QObject::disconnect(channels[i].socket, Q_NULLPTR, &channels[i], Q_NULLPTR);
|
||||
channels[i].socket->close();
|
||||
delete channels[i].socket;
|
||||
}
|
||||
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
|
||||
@@ -417,6 +430,20 @@ index 566abf2126..5b9c714ffa 100644
|
||||
fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute);
|
||||
fd->weight = QFont::Normal;
|
||||
fd->style = QFont::StyleNormal;
|
||||
diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
index 7b459584ea..2ed2fd9b3b 100644
|
||||
--- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
+++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm
|
||||
@@ -764,7 +764,8 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl
|
||||
|
||||
QFixed QCoreTextFontEngine::emSquareSize() const
|
||||
{
|
||||
- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont)));
|
||||
+ // Patch: Fix build for Xcode 9.3.1.
|
||||
+ return QFixed(int(CTFontGetUnitsPerEm(ctfont)));
|
||||
}
|
||||
|
||||
QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const
|
||||
diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro
|
||||
index 86bdd4729b..9b9c8ded08 100644
|
||||
--- a/src/plugins/platforminputcontexts/compose/compose.pro
|
||||
|
||||
@@ -40,7 +40,7 @@ lineWidth: 1px;
|
||||
|
||||
defaultTextPalette: TextPalette {
|
||||
linkFg: windowActiveTextFg;
|
||||
monoFg: windowSubTextFg;
|
||||
monoFg: msgInMonoFg;
|
||||
selectBg: msgInBgSelected;
|
||||
selectFg: transparent; // use painter current pen instead
|
||||
selectLinkFg: historyLinkInFgSelected;
|
||||
@@ -172,6 +172,21 @@ inFwdTextPaletteSelected: TextPalette(defaultTextPalette) {
|
||||
outFwdTextPaletteSelected: TextPalette(defaultTextPalette) {
|
||||
linkFg: msgOutServiceFgSelected;
|
||||
}
|
||||
inReplyTextPalette: TextPalette(inTextPalette) {
|
||||
linkFg: msgInDateFg;
|
||||
}
|
||||
inReplyTextPaletteSelected: TextPalette(inTextPaletteSelected) {
|
||||
linkFg: msgInDateFgSelected;
|
||||
}
|
||||
outReplyTextPalette: TextPalette(outTextPalette) {
|
||||
linkFg: msgOutDateFg;
|
||||
}
|
||||
outReplyTextPaletteSelected: TextPalette(outTextPaletteSelected) {
|
||||
linkFg: msgOutDateFgSelected;
|
||||
}
|
||||
imgReplyTextPalette: TextPalette(defaultTextPalette) {
|
||||
linkFg: msgImgReplyBarColor;
|
||||
}
|
||||
inSemiboldPalette: TextPalette(inTextPalette) {
|
||||
linkFg: msgInServiceFg;
|
||||
selectFg: msgInServiceFgSelected;
|
||||
@@ -182,6 +197,9 @@ outSemiboldPalette: TextPalette(outTextPalette) {
|
||||
selectFg: msgOutServiceFgSelected;
|
||||
selectLinkFg: msgOutServiceFgSelected;
|
||||
}
|
||||
historyComposeAreaPalette: TextPalette(defaultTextPalette) {
|
||||
linkFg: historyComposeAreaFgService;
|
||||
}
|
||||
|
||||
mediaCaptionSkip: 5px;
|
||||
mediaInBubbleSkip: 5px;
|
||||
|
||||
BIN
Telegram/Resources/icons/passport_authorize.png
Normal file
BIN
Telegram/Resources/icons/passport_authorize.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 362 B |
BIN
Telegram/Resources/icons/passport_authorize@2x.png
Normal file
BIN
Telegram/Resources/icons/passport_authorize@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 756 B |
BIN
Telegram/Resources/icons/passport_empty.png
Normal file
BIN
Telegram/Resources/icons/passport_empty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 346 B |
BIN
Telegram/Resources/icons/passport_empty@2x.png
Normal file
BIN
Telegram/Resources/icons/passport_empty@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 688 B |
BIN
Telegram/Resources/icons/passport_password_setup.png
Normal file
BIN
Telegram/Resources/icons/passport_password_setup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
Telegram/Resources/icons/passport_password_setup@2x.png
Normal file
BIN
Telegram/Resources/icons/passport_password_setup@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_quit_from_tray" = "Quit Telegram";
|
||||
"lng_tray_icon_text" = "Telegram is still running here,\nyou can change this from settings page.\nIf this icon disappears from tray menu,\nyou can drag it here from hidden icons.";
|
||||
|
||||
// For lng_month_year or plain month name.
|
||||
"lng_month1" = "January";
|
||||
"lng_month2" = "February";
|
||||
"lng_month3" = "March";
|
||||
@@ -36,6 +37,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_month11" = "November";
|
||||
"lng_month12" = "December";
|
||||
|
||||
// For lng_month_day and lng_month_day_year.
|
||||
"lng_month_day1" = "January";
|
||||
"lng_month_day2" = "February";
|
||||
"lng_month_day3" = "March";
|
||||
"lng_month_day4" = "April";
|
||||
"lng_month_day5" = "May";
|
||||
"lng_month_day6" = "June";
|
||||
"lng_month_day7" = "July";
|
||||
"lng_month_day8" = "August";
|
||||
"lng_month_day9" = "September";
|
||||
"lng_month_day10" = "October";
|
||||
"lng_month_day11" = "November";
|
||||
"lng_month_day12" = "December";
|
||||
|
||||
"lng_month1_small" = "Jan";
|
||||
"lng_month2_small" = "Feb";
|
||||
"lng_month3_small" = "Mar";
|
||||
@@ -123,6 +138,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_error_cant_add_admin_invite" = "Sorry, you can't add this user as an admin because they are not a member of this group and you are not allowed to invite them.";
|
||||
"lng_error_cant_add_admin_unban" = "Sorry, you can't add this user as an admin because they are in the blacklist and you can't unban them.";
|
||||
"lng_error_cant_ban_admin" = "Sorry, you can't ban this user because they are an admin in this group and you are not allowed to demote them.";
|
||||
"lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group.";
|
||||
"lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel.";
|
||||
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
|
||||
"lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?";
|
||||
"lng_sure_add_admin_unban" = "This user is currently restricted or banned. Are you sure you want to unban and promote them?";
|
||||
@@ -179,19 +196,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_signin_title" = "Cloud password check";
|
||||
"lng_signin_desc" = "Please enter your cloud password.";
|
||||
"lng_signin_recover_desc" = "Please enter the code from the e-mail\n{email}";
|
||||
"lng_signin_recover_desc" = "Please enter the code from the email\n{email}";
|
||||
"lng_signin_password" = "Your cloud password";
|
||||
"lng_signin_code" = "Code from e-mail";
|
||||
"lng_signin_code" = "Code from email";
|
||||
"lng_signin_recover" = "Forgot password?";
|
||||
"lng_signin_recover_title" = "Password reset";
|
||||
"lng_signin_hint" = "Hint: {password_hint}";
|
||||
"lng_signin_recover_hint" = "Code was sent to {recover_email}";
|
||||
"lng_signin_bad_password" = "You have entered a wrong password.";
|
||||
"lng_signin_wrong_code" = "You have entered an invalid code.";
|
||||
"lng_signin_try_password" = "Having trouble accessing your e-mail?";
|
||||
"lng_signin_try_password" = "Having trouble accessing your email?";
|
||||
"lng_signin_password_removed" = "Your cloud password was disabled.\nYou can set up a new one in Settings.";
|
||||
"lng_signin_no_email_forgot" = "Since you didn't provide a recovery e-mail when setting up your password, your remaining options are either to remember your password or to reset your account.";
|
||||
"lng_signin_cant_email_forgot" = "If you can't restore access to the e-mail, your remaining options are either to remember your password or to reset your account.";
|
||||
"lng_signin_no_email_forgot" = "Since you didn't provide a recovery email when setting up your password, your remaining options are either to remember your password or to reset your account.";
|
||||
"lng_signin_cant_email_forgot" = "If you can't restore access to the email, your remaining options are either to remember your password or to reset your account.";
|
||||
"lng_signin_reset_account" = "Reset your account";
|
||||
"lng_signin_sure_reset" = "Warning!\n\nYou will lose all your chats and messages, along with any media and files you shared!\n\nDo you want to reset your account?";
|
||||
"lng_signin_reset" = "Reset";
|
||||
@@ -296,6 +313,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_settings_section_chat_settings" = "Chat Settings";
|
||||
"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_view_emojis" = "View list";
|
||||
"lng_settings_send_enter" = "Send by Enter";
|
||||
@@ -391,18 +409,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_hint" = "Enter password hint";
|
||||
"lng_cloud_password_change_hint" = "Enter new password hint";
|
||||
"lng_cloud_password_bad" = "Password and hint cannot be the same.";
|
||||
"lng_cloud_password_email" = "Enter recovery e-mail";
|
||||
"lng_cloud_password_bad_email" = "Incorrect e-mail, please try other.";
|
||||
"lng_cloud_password_email" = "Enter recovery email";
|
||||
"lng_cloud_password_bad_email" = "Incorrect email, please try other.";
|
||||
"lng_cloud_password_about" = "This password will be asked when you log in on a new device in addition to the SMS code.";
|
||||
"lng_cloud_password_about_recover" = "Warning! Are you sure you don't want to\nadd a password recovery e-mail?\n\nIf you forget your password, you will\nlose access to your Telegram account.";
|
||||
"lng_cloud_password_skip_email" = "Skip e-mail";
|
||||
"lng_cloud_password_almost" = "A confirmation link was sent to the\ne-mail you provided. Two-step verification will be enabled as soon as you follow that link.";
|
||||
"lng_cloud_password_about_recover" = "Warning! Are you sure you don't want to\nadd a password recovery email?\n\nIf you forget your password, you will\nlose access to your Telegram account.";
|
||||
"lng_cloud_password_skip_email" = "Skip email";
|
||||
"lng_cloud_password_almost" = "A confirmation link was sent to the\nemail you provided. Two-step verification will be enabled as soon as you follow that link.";
|
||||
"lng_cloud_password_was_set" = "Two-step verification enabled.";
|
||||
"lng_cloud_password_updated" = "Your cloud password was updated.";
|
||||
"lng_cloud_password_removed" = "Two-step verification was disabled.";
|
||||
"lng_cloud_password_differ" = "Passwords do not match";
|
||||
"lng_cloud_password_wrong" = "Wrong cloud password";
|
||||
"lng_cloud_password_is_same" = "Password was not changed";
|
||||
"lng_cloud_password_passport_losing" = "Warning! All data saved in your Telegram Passport will be lost!";
|
||||
|
||||
"lng_connection_type" = "Connection type:";
|
||||
"lng_connection_auto_connecting" = "Default (connecting...)";
|
||||
@@ -427,10 +446,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_proxy_about" = "Proxy servers may be helpful in accessing Telegram if there is no connection in a specific region.";
|
||||
"lng_proxy_add" = "Add proxy";
|
||||
"lng_proxy_share" = "Share";
|
||||
"lng_proxy_online" = "online";
|
||||
"lng_proxy_online" = "connected";
|
||||
"lng_proxy_checking" = "checking";
|
||||
"lng_proxy_connecting" = "connecting";
|
||||
"lng_proxy_available" = "available (ping: {ping}ms)";
|
||||
"lng_proxy_available" = "available, {ping} ms ping";
|
||||
"lng_proxy_unavailable" = "not available";
|
||||
"lng_proxy_edit" = "Edit proxy";
|
||||
"lng_proxy_menu_edit" = "Edit";
|
||||
@@ -441,6 +460,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_proxy_credentials_optional" = "Credentials (optional)";
|
||||
"lng_proxy_credentials" = "Credentials";
|
||||
"lng_proxy_description" = "Your saved proxy list will be here.";
|
||||
"lng_proxy_sponsor" = "Proxy sponsor";
|
||||
"lng_proxy_sponsor_about" = "This channel is shown by your proxy server.\nTo remove this channel from your chats list,\ndisable the proxy in Telegram Settings.";
|
||||
"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_last_seen_privacy" = "Last seen privacy";
|
||||
@@ -551,6 +573,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_profile_actions_section" = "Actions";
|
||||
"lng_profile_bot_settings" = "Bot Settings";
|
||||
"lng_profile_bot_help" = "Bot Help";
|
||||
"lng_profile_bot_privacy" = "Bot Privacy Policy";
|
||||
"lng_profile_invite_link_section" = "Invite link";
|
||||
"lng_profile_create_public_link" = "Create public link";
|
||||
"lng_profile_edit_public_link" = "Edit public link";
|
||||
@@ -690,6 +713,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_report_title" = "Report channel";
|
||||
"lng_report_group_title" = "Report group";
|
||||
"lng_report_bot_title" = "Report bot";
|
||||
"lng_report_message_title" = "Report message";
|
||||
"lng_report_reason_spam" = "Spam";
|
||||
"lng_report_reason_violence" = "Violence";
|
||||
"lng_report_reason_pornography" = "Pornography";
|
||||
@@ -817,6 +841,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_action_took_screenshot" = "{from} took a screenshot!";
|
||||
"lng_action_you_took_screenshot" = "You took a screenshot!";
|
||||
"lng_action_bot_allowed_from_domain" = "You allowed this bot to message you when you logged in on {domain}.";
|
||||
"lng_action_secure_values_sent" = "{user} received the following documents: {documents}";
|
||||
"lng_action_secure_personal_details" = "personal details";
|
||||
"lng_action_secure_proof_of_identity" = "proof of identity";
|
||||
"lng_action_secure_address" = "address";
|
||||
"lng_action_secure_proof_of_address" = "proof of address";
|
||||
"lng_action_secure_phone" = "phone number";
|
||||
"lng_action_secure_email" = "email address";
|
||||
|
||||
"lng_ttl_photo_received" = "{from} sent you a self-destructing photo. Please view it on your mobile.";
|
||||
"lng_ttl_photo_sent" = "You sent a self-destructing photo.";
|
||||
@@ -1105,6 +1136,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_forward_msg" = "Forward Message";
|
||||
"lng_context_delete_msg" = "Delete Message";
|
||||
"lng_context_select_msg" = "Select Message";
|
||||
"lng_context_report_msg" = "Report Message";
|
||||
"lng_context_pin_msg" = "Pin Message";
|
||||
"lng_context_unpin_msg" = "Unpin Message";
|
||||
"lng_context_cancel_upload" = "Cancel Upload";
|
||||
@@ -1247,6 +1279,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insert Unicode control character";
|
||||
"lng_menu_formatting" = "Formatting";
|
||||
"lng_menu_formatting_bold" = "Bold";
|
||||
"lng_menu_formatting_italic" = "Italic";
|
||||
"lng_menu_formatting_monospace" = "Monospace";
|
||||
"lng_menu_formatting_link_create" = "Create link";
|
||||
"lng_menu_formatting_link_edit" = "Edit link";
|
||||
"lng_menu_formatting_clear" = "Plain text";
|
||||
"lng_formatting_link_create_title" = "Create link";
|
||||
"lng_formatting_link_edit_title" = "Create link";
|
||||
"lng_formatting_link_text" = "Text";
|
||||
"lng_formatting_link_url" = "URL";
|
||||
"lng_formatting_link_create" = "Create";
|
||||
|
||||
"lng_full_name" = "{first_name} {last_name}";
|
||||
|
||||
@@ -1472,6 +1516,143 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_info_feed_is_default" = "Group new channels";
|
||||
"lng_info_feed_channels" = "Channels";
|
||||
|
||||
"lng_terms_signup" = "By signing up,\nyou agree to the {link}.";
|
||||
"lng_terms_signup_link" = "Terms of Service";
|
||||
"lng_terms_header" = "Terms of Service";
|
||||
"lng_terms_age#one" = "I confirm that I am {count} or over";
|
||||
"lng_terms_age#other" = "I confirm that I am {count} or over";
|
||||
"lng_terms_agree" = "Agree & Continue";
|
||||
"lng_terms_decline" = "Decline";
|
||||
"lng_terms_signup_sorry" = "We're very sorry, but this means you can't sign up for Telegram.\n\nUnlike others, we don't use your data for ad targeting or other commercial purposes. Telegram only stores the information it needs to function as a feature-rich cloud service. You can adjust how we use your data in Privacy & Security settings.\n\nBut if you're generally not OK with Telegram's modest needs, it won't be possible for us to provide this service.";
|
||||
"lng_terms_update_sorry" = "We're very sorry, but this means we must part ways here. Unlike others, we don't use your data for ad targeting or other commercial purposes. Telegram only stores the information it needs to function as a feature-rich cloud service. You can adjust how we use your data in Privacy & Security settings.\n\nBut if you're generally not OK with Telegram's modest needs, it won't be possible for us to provide this service. You can deactivate your account now — or look around some more and deactivate it later if you feel you're not happy with the way we use your data. How does that sound?";
|
||||
"lng_terms_decline_and_delete" = "Decline & Delete";
|
||||
"lng_terms_back" = "Back";
|
||||
"lng_terms_delete_warning" = "Warning, this will irreversibly delete your Telegram account along with all the data you store in the Telegram cloud.\n\nWe will provide a tool to download your data before June, 23 – so you may want to wait a little before deleting.";
|
||||
"lng_terms_delete_now" = "Delete now";
|
||||
"lng_terms_agree_to_proceed" = "Please agree and proceed to {bot}.";
|
||||
|
||||
"lng_date_input_day" = "Day";
|
||||
"lng_date_input_month" = "Month";
|
||||
"lng_date_input_year" = "Year";
|
||||
|
||||
"lng_passport_title" = "Telegram passport";
|
||||
"lng_passport_request1" = "{bot} requests access to your personal data";
|
||||
"lng_passport_request2" = "to sign you up for their services";
|
||||
"lng_passport_create_password" = "Please create a password which will be used\nto encrypt your personal data.";
|
||||
"lng_passport_about_password" = "This password will also be required whenever\nyou log in to a new device.";
|
||||
"lng_passport_password_create" = "Create a password";
|
||||
"lng_passport_link_sent" = "A confirmation link was sent to your email\n{email}";
|
||||
"lng_passport_stop_password_sure" = "Are you sure you want to cancel setting up your password?";
|
||||
"lng_passport_password_placeholder" = "Your password";
|
||||
"lng_passport_next" = "Next";
|
||||
"lng_passport_password_wrong" = "The password you entered is not valid.";
|
||||
"lng_passport_header" = "Requested information";
|
||||
"lng_passport_identity_title" = "Identity document";
|
||||
"lng_passport_identity_description" = "Upload a scan of your passport or another ID";
|
||||
"lng_passport_identity_passport" = "Passport";
|
||||
"lng_passport_identity_passport_upload" = "Upload a scan of your passport";
|
||||
"lng_passport_identity_card" = "Identity card";
|
||||
"lng_passport_identity_card_upload" = "Upload a scan of your identity card";
|
||||
"lng_passport_identity_license" = "Driver's license";
|
||||
"lng_passport_identity_license_upload" = "Upload a scan of your driver's license";
|
||||
"lng_passport_identity_internal" = "Internal passport";
|
||||
"lng_passport_identity_internal_upload" = "Upload a scan of your internal passport";
|
||||
"lng_passport_identity_about" = "The document must contain your photograph, first and last name, date of birth, document number, country of issue, and expiry date.";
|
||||
"lng_passport_address_title" = "Residential address";
|
||||
"lng_passport_address_description" = "Upload a proof of your address";
|
||||
"lng_passport_address_bill" = "Utility bill";
|
||||
"lng_passport_address_bill_upload" = "Upload a scan of your utility bill";
|
||||
"lng_passport_address_statement" = "Bank statement";
|
||||
"lng_passport_address_statement_upload" = "Upload a scan of your bank statement";
|
||||
"lng_passport_address_agreement" = "Tenancy agreement";
|
||||
"lng_passport_address_agreement_upload" = "Upload a scan of your tenancy agreement";
|
||||
"lng_passport_address_registration" = "Passport registration";
|
||||
"lng_passport_address_registration_upload" = "Upload a scan of your passport registration";
|
||||
"lng_passport_address_temporary" = "Temporary registration";
|
||||
"lng_passport_address_temporary_upload" = "Upload a scan of your temporary registration";
|
||||
"lng_passport_address_about" = "To confirm your address, please upload a scan or photo of the selected document (all pages).";
|
||||
"lng_passport_document_type" = "Please choose the type of your document:";
|
||||
"lng_passport_upload_document" = "Upload document";
|
||||
"lng_passport_phone_title" = "Phone number";
|
||||
"lng_passport_phone_description" = "Enter your phone number";
|
||||
"lng_passport_email_title" = "Email";
|
||||
"lng_passport_email_description" = "Enter your email address";
|
||||
"lng_passport_accept_allow" = "You accept the {policy} and allow their {bot} to send you messages.";
|
||||
"lng_passport_allow" = "You allow {bot} to send you messages.";
|
||||
"lng_passport_policy" = "{bot} privacy policy";
|
||||
"lng_passport_authorize" = "Authorize";
|
||||
"lng_passport_form_error" = "Could not get authorization form.";
|
||||
"lng_passport_save_value" = "Save";
|
||||
"lng_passport_saving" = "Saving...";
|
||||
"lng_passport_uploading" = "Uploading...";
|
||||
"lng_passport_scan_index" = "Scan {index}";
|
||||
"lng_passport_upload_scans" = "Upload scans";
|
||||
"lng_passport_upload_more" = "Upload additional scans";
|
||||
"lng_passport_selfie_title" = "Selfie";
|
||||
"lng_passport_selfie_name" = "Photo";
|
||||
"lng_passport_selfie_description" = "Upload a photo of yourself holding your document. Make sure the ID and your face are clearly visible.";
|
||||
"lng_passport_upload_selfie" = "Upload selfie";
|
||||
"lng_passport_front_side_title" = "Front side";
|
||||
"lng_passport_front_side_name" = "Scan";
|
||||
"lng_passport_front_side_description" = "Upload front side of your document.";
|
||||
"lng_passport_upload_front_side" = "Upload front side scan";
|
||||
"lng_passport_reverse_side_title" = "Reverse side";
|
||||
"lng_passport_reverse_side_name" = "Scan";
|
||||
"lng_passport_reverse_side_description" = "Upload reverse side of your document.";
|
||||
"lng_passport_upload_reverse_side" = "Upload reverse side scan";
|
||||
"lng_passport_personal_details" = "Personal details";
|
||||
"lng_passport_personal_details_enter" = "Enter your personal details";
|
||||
"lng_passport_choose_image" = "Choose scan image";
|
||||
"lng_passport_delete_scan_undo" = "Undo";
|
||||
"lng_passport_scan_uploaded" = "Uploaded on {date}";
|
||||
"lng_passport_first_name" = "Name";
|
||||
"lng_passport_last_name" = "Surname";
|
||||
"lng_passport_birth_date" = "Date of birth";
|
||||
"lng_passport_gender" = "Gender";
|
||||
"lng_passport_gender_male" = "Male";
|
||||
"lng_passport_gender_female" = "Female";
|
||||
"lng_passport_country" = "Country";
|
||||
"lng_passport_residence_country" = "Residence";
|
||||
"lng_passport_country_choose" = "Choose country";
|
||||
"lng_passport_document_number" = "Card Number";
|
||||
"lng_passport_expiry_date" = "Expiry date";
|
||||
"lng_passport_address" = "Address";
|
||||
"lng_passport_address_enter" = "Enter your address";
|
||||
"lng_passport_street" = "Street";
|
||||
"lng_passport_city" = "City";
|
||||
"lng_passport_state" = "State";
|
||||
"lng_passport_postcode" = "Postcode";
|
||||
"lng_passport_use_existing" = "USE {existing}";
|
||||
"lng_passport_use_existing_phone" = "Use the same phone number as on Telegram.";
|
||||
"lng_passport_new_phone" = "Or enter a new phone number";
|
||||
"lng_passport_new_phone_code" = "Note: You will receive a confirmation code on the phone number you provide.";
|
||||
"lng_passport_use_existing_email" = "Use the same email address as on Telegram.";
|
||||
"lng_passport_new_email" = "Or enter a new email";
|
||||
"lng_passport_new_email_code" = "Note: You will receive a confirmation code to the email address you provide.";
|
||||
"lng_passport_confirm_phone" = "We've sent an SMS with a confirmation code to your phone {phone}.";
|
||||
"lng_passport_confirm_email" = "We've sent a confirmation code to your email {email}.";
|
||||
"lng_passport_sure_cancel" = "If you continue, your changes will be lost.";
|
||||
"lng_passport_scans_limit_reached" = "Sorry, that's too many scans for one document.";
|
||||
"lng_passport_delete_document" = "Delete document";
|
||||
"lng_passport_delete_document_sure" = "Are you sure you want to delete this document?";
|
||||
"lng_passport_delete_details" = "Delete personal details";
|
||||
"lng_passport_delete_details_sure" = "Are you sure you want to delete your personal details?";
|
||||
"lng_passport_delete_address" = "Delete address information";
|
||||
"lng_passport_delete_address_sure" = "Are you sure you want to delete your address information?";
|
||||
"lng_passport_delete_email" = "Delete email";
|
||||
"lng_passport_delete_email_sure" = "Are you sure you want to delete your email address?";
|
||||
"lng_passport_delete_phone" = "Delete phone number";
|
||||
"lng_passport_delete_phone_sure" = "Are you sure you want to delete your phone number?";
|
||||
"lng_passport_success" = "Authorization successfull!";
|
||||
"lng_passport_stop_sure" = "Are you sure you want to stop this authorization?";
|
||||
"lng_passport_stop" = "Stop";
|
||||
"lng_passport_restart_sure" = "An unexpected error has occurred. Perhaps some changes were made from a different Telegram application. Would you like to restart this authorization?";
|
||||
"lng_passport_restart" = "Restart";
|
||||
"lng_passport_error_too_large" = "This file is too large.";
|
||||
"lng_passport_error_bad_size" = "This image has bad dimensions.";
|
||||
"lng_passport_error_cant_read" = "Can't read this file. Please choose an image.";
|
||||
"lng_passport_bad_name" = "Use latin characters only.";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
||||
@@ -193,19 +193,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_signin_title" = "Contraseña";
|
||||
"lng_signin_desc" = "Por favor, pon tu contraseña.";
|
||||
"lng_signin_recover_desc" = "Por favor, pon el código desde el e-mail\n{email}";
|
||||
"lng_signin_recover_desc" = "Por favor, pon el código desde el email\n{email}";
|
||||
"lng_signin_password" = "Tu contraseña";
|
||||
"lng_signin_code" = "Código desde el e-mail";
|
||||
"lng_signin_code" = "Código desde el email";
|
||||
"lng_signin_recover" = "¿Olvidaste la contraseña?";
|
||||
"lng_signin_recover_title" = "Restablecer contraseña";
|
||||
"lng_signin_hint" = "Pista: {password_hint}";
|
||||
"lng_signin_recover_hint" = "El código fue enviado a {recover_email}";
|
||||
"lng_signin_bad_password" = "Pusiste la contraseña equivocada.";
|
||||
"lng_signin_wrong_code" = "Has ingresado un código inválido.";
|
||||
"lng_signin_try_password" = "¿Tienes problemas para acceder a tu e-mail?";
|
||||
"lng_signin_try_password" = "¿Tienes problemas para acceder a tu email?";
|
||||
"lng_signin_password_removed" = "Tu contraseña fue desactivada.\nPuedes configurar una nueva en Ajustes.";
|
||||
"lng_signin_no_email_forgot" = "Como no estableciste un e-mail de recuperación \ncuando configuraste tu contraseña, las opciones restantes son recordar tu contraseña o restablecer tu cuenta.";
|
||||
"lng_signin_cant_email_forgot" = "Si no puedes acceder a tu e-mail, las opciones restantes son recordar tu contraseña o restablecer tu cuenta.";
|
||||
"lng_signin_no_email_forgot" = "Como no estableciste un email de recuperación \ncuando configuraste tu contraseña, las opciones restantes son recordar tu contraseña o restablecer tu cuenta.";
|
||||
"lng_signin_cant_email_forgot" = "Si no puedes acceder a tu email, las opciones restantes son recordar tu contraseña o restablecer tu cuenta.";
|
||||
"lng_signin_reset_account" = "Restablecer tu cuenta";
|
||||
"lng_signin_sure_reset" = "¡Advertencia!\n\n¡Perderás todos tus chats y mensajes, junto con la multimedia y archivos compartidos!\n\n¿Quieres restablecer tu cuenta?";
|
||||
"lng_signin_reset" = "Restablecer";
|
||||
@@ -406,12 +406,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_hint" = "Pon una pista para la contraseña";
|
||||
"lng_cloud_password_change_hint" = "Pon una pista para la contraseña";
|
||||
"lng_cloud_password_bad" = "La contraseña y la pista no pueden ser iguales.";
|
||||
"lng_cloud_password_email" = "Pon un e-mail de recuperación";
|
||||
"lng_cloud_password_bad_email" = "E-mail incorrecto. Por favor, prueba otro.";
|
||||
"lng_cloud_password_email" = "Pon un email de recuperación";
|
||||
"lng_cloud_password_bad_email" = "email incorrecto. Por favor, prueba otro.";
|
||||
"lng_cloud_password_about" = "Necesitarás la contraseña al iniciar sesión, además del código de activación.";
|
||||
"lng_cloud_password_about_recover" = "¡Advertencia! ¿No quieres añadir un e-mail \nde recuperación para la contraseña?\n\nSi olvidas tu contraseña, perderás\nel acceso a tu cuenta de Telegram.";
|
||||
"lng_cloud_password_skip_email" = "Omitir e-mail";
|
||||
"lng_cloud_password_almost" = "Un enlace de confirmación fue enviado \nal e-mail que estableciste. La verificación en dos pasos se activará en cuanto sigas ese enlace.";
|
||||
"lng_cloud_password_about_recover" = "¡Advertencia! ¿No quieres añadir un email \nde recuperación para la contraseña?\n\nSi olvidas tu contraseña, perderás\nel acceso a tu cuenta de Telegram.";
|
||||
"lng_cloud_password_skip_email" = "Omitir email";
|
||||
"lng_cloud_password_almost" = "Un enlace de confirmación fue enviado \nal email que estableciste. La verificación en dos pasos se activará en cuanto sigas ese enlace.";
|
||||
"lng_cloud_password_was_set" = "Verificación en dos pasos activada.";
|
||||
"lng_cloud_password_updated" = "Tu contraseña fue actualizada.";
|
||||
"lng_cloud_password_removed" = "Verificación en dos pasos desactivada.";
|
||||
@@ -1044,7 +1044,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_context_copy_link" = "Copiar enlace";
|
||||
"lng_context_copy_post_link" = "Copiar enlace de la publicación";
|
||||
"lng_context_copy_email" = "Copiar e-mail";
|
||||
"lng_context_copy_email" = "Copiar email";
|
||||
"lng_context_copy_hashtag" = "Copiar hashtag";
|
||||
"lng_context_copy_mention" = "Copiar alias";
|
||||
"lng_context_save_image" = "Guardar como...";
|
||||
|
||||
@@ -193,19 +193,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_signin_title" = "Verifica password";
|
||||
"lng_signin_desc" = "Per favore inserisci la tua password.";
|
||||
"lng_signin_recover_desc" = "Per favore inserisci il codice dall'e-mail\n{email}";
|
||||
"lng_signin_recover_desc" = "Per favore inserisci il codice dall'email\n{email}";
|
||||
"lng_signin_password" = "La tua password";
|
||||
"lng_signin_code" = "Codice dall'e-mail";
|
||||
"lng_signin_code" = "Codice dall'email";
|
||||
"lng_signin_recover" = "Password dimenticata?";
|
||||
"lng_signin_recover_title" = "Ripristino password";
|
||||
"lng_signin_hint" = "Suggerimento: {password_hint}";
|
||||
"lng_signin_recover_hint" = "Il codice è stato inviato a {recover_email}";
|
||||
"lng_signin_bad_password" = "Hai inserito una password errata.";
|
||||
"lng_signin_wrong_code" = "Hai inserito un codice non valido.";
|
||||
"lng_signin_try_password" = "Hai problemi ad accedere alla tua e-mail?";
|
||||
"lng_signin_try_password" = "Hai problemi ad accedere alla tua email?";
|
||||
"lng_signin_password_removed" = "La tua password è stata disattivata.\nNe puoi impostare una nuova dalle Impostazioni.";
|
||||
"lng_signin_no_email_forgot" = "Siccome non hai fornito un'email di recupero quando hai inserito la password, non ti resta che ricordarti la password o ripristinare il tuo account.";
|
||||
"lng_signin_cant_email_forgot" = "Se non puoi recuperare l'accesso all'e-mail, non ti resta che ricordarti la password o ripristinare il tuo account.";
|
||||
"lng_signin_cant_email_forgot" = "Se non puoi recuperare l'accesso all'email, non ti resta che ricordarti la password o ripristinare il tuo account.";
|
||||
"lng_signin_reset_account" = "Ripristina il tuo account";
|
||||
"lng_signin_sure_reset" = "Attenzione!\n\nPerderai tutte le chat e i messaggi, insieme a tutti i media e i file condivisi!\n\nVuoi ripristinare il tuo account?";
|
||||
"lng_signin_reset" = "Ripristina";
|
||||
@@ -407,11 +407,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_change_hint" = "Inserisci un nuovo suggerimento";
|
||||
"lng_cloud_password_bad" = "Password e suggerimento devono essere diversi.";
|
||||
"lng_cloud_password_email" = "Inserisci email di recupero";
|
||||
"lng_cloud_password_bad_email" = "E-mail non valida, riprova con un'altra.";
|
||||
"lng_cloud_password_bad_email" = "email non valida, riprova con un'altra.";
|
||||
"lng_cloud_password_about" = "La password sarà richiesta quando ti connetti da un nuovo dispositivo insieme al codice.";
|
||||
"lng_cloud_password_about_recover" = "Attenzione! Sicuro di non voler\naggiungere un'e-mail di recupero?\n\nSe dimentichi la tua password, perderai\nl'accesso al tuo account Telegram.";
|
||||
"lng_cloud_password_skip_email" = "Salta e-mail";
|
||||
"lng_cloud_password_almost" = "Abbiamo inviato un link di conferma\nall'e-mail che ci hai fornito. La verifica in due passaggi sarà attivata non appena aprirai quel link.";
|
||||
"lng_cloud_password_about_recover" = "Attenzione! Sicuro di non voler\naggiungere un'email di recupero?\n\nSe dimentichi la tua password, perderai\nl'accesso al tuo account Telegram.";
|
||||
"lng_cloud_password_skip_email" = "Salta email";
|
||||
"lng_cloud_password_almost" = "Abbiamo inviato un link di conferma\nall'email che ci hai fornito. La verifica in due passaggi sarà attivata non appena aprirai quel link.";
|
||||
"lng_cloud_password_was_set" = "Verifica in due passaggi abilitata.";
|
||||
"lng_cloud_password_updated" = "La tua password è stata aggiornata.";
|
||||
"lng_cloud_password_removed" = "La verifica in due passaggi è stata disattivata.";
|
||||
|
||||
@@ -193,19 +193,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_signin_title" = "Cloud-wachtwoord:";
|
||||
"lng_signin_desc" = "Cloud-wachtwoord invoeren";
|
||||
"lng_signin_recover_desc" = "Voer de code uit de e-mail in\n{email}";
|
||||
"lng_signin_recover_desc" = "Voer de code uit de email in\n{email}";
|
||||
"lng_signin_password" = "Je cloud-wachtwoord";
|
||||
"lng_signin_code" = "Code uit de e-mail";
|
||||
"lng_signin_code" = "Code uit de email";
|
||||
"lng_signin_recover" = "Wachtwoord vergeten?";
|
||||
"lng_signin_recover_title" = "Wachtwoord reset";
|
||||
"lng_signin_hint" = "Hint: {password_hint}";
|
||||
"lng_signin_recover_hint" = "De code is verstuurd naar {recover_email}";
|
||||
"lng_signin_bad_password" = "Ongeldig wachtwoord.";
|
||||
"lng_signin_wrong_code" = "Je hebt een ongeldige code ingevoerd.";
|
||||
"lng_signin_try_password" = "Kun je je e-mail niet benaderen?";
|
||||
"lng_signin_try_password" = "Kun je je email niet benaderen?";
|
||||
"lng_signin_password_removed" = "Je cloud-wachtwoord is uitgeschakeld.\nVia instellingen kun je een nieuwe instellen.";
|
||||
"lng_signin_no_email_forgot" = "Omdat je geen herstel-e-mailadres hebt opgegeven voor je wachtwoord zul je bij verlies van je wachtwoord je account moeten resetten.";
|
||||
"lng_signin_cant_email_forgot" = "Als je geen toegang meer hebt tot je e-mail en je ingestelde wachtwoord zul je je account moeten resetten.";
|
||||
"lng_signin_no_email_forgot" = "Omdat je geen herstel-emailadres hebt opgegeven voor je wachtwoord zul je bij verlies van je wachtwoord je account moeten resetten.";
|
||||
"lng_signin_cant_email_forgot" = "Als je geen toegang meer hebt tot je email en je ingestelde wachtwoord zul je je account moeten resetten.";
|
||||
"lng_signin_reset_account" = "Account resetten";
|
||||
"lng_signin_sure_reset" = "Let op:\n\nAl je chats, berichten en alle andere data gaan verloren als je verder gaat!\n\nEcht je account resetten?";
|
||||
"lng_signin_reset" = "Reset";
|
||||
@@ -406,12 +406,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_hint" = "Wachtwoordhint invoeren";
|
||||
"lng_cloud_password_change_hint" = "Wachtwoordhint invoeren";
|
||||
"lng_cloud_password_bad" = "De hint moet anders zijn dan je wachtwoord.";
|
||||
"lng_cloud_password_email" = "Herstel-e-mailadres invoeren";
|
||||
"lng_cloud_password_bad_email" = "Ongeldig e-mailadres, probeer een andere.";
|
||||
"lng_cloud_password_email" = "Herstel-emailadres invoeren";
|
||||
"lng_cloud_password_bad_email" = "Ongeldig emailadres, probeer een andere.";
|
||||
"lng_cloud_password_about" = "Dit wachtwoord is nodig als je inlogt vanaf een nieuw apparaat, naast de SMS-code.";
|
||||
"lng_cloud_password_about_recover" = "Let op: Echt geen herstel-emailadres\nopgeven voor je wachtwoord?\n\nBij verlies van je wachtwoord ben je\nook de toegang tot Telegram kwijt.";
|
||||
"lng_cloud_password_skip_email" = "E-mail overslaan";
|
||||
"lng_cloud_password_almost" = "Een bevestigingslink is naar het e-mailadres verstuurd. \ntwee-staps-verificatie is actief na het klikken van de e-mail-link.";
|
||||
"lng_cloud_password_skip_email" = "email overslaan";
|
||||
"lng_cloud_password_almost" = "Een bevestigingslink is naar het emailadres verstuurd. \ntwee-staps-verificatie is actief na het klikken van de email-link.";
|
||||
"lng_cloud_password_was_set" = "Twee-staps-verificatie ingeschakeld.";
|
||||
"lng_cloud_password_updated" = "Je cloud-wachtwoord is gewijzigd.";
|
||||
"lng_cloud_password_removed" = "Twee-staps-verificatie uitgeschakeld.";
|
||||
@@ -1044,7 +1044,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_context_copy_link" = "Link kopiëren";
|
||||
"lng_context_copy_post_link" = "Berichtlink kopiëren";
|
||||
"lng_context_copy_email" = "E-mailadres kopiëren";
|
||||
"lng_context_copy_email" = "emailadres kopiëren";
|
||||
"lng_context_copy_hashtag" = "Hashtag kopiëren";
|
||||
"lng_context_copy_mention" = "Gebruikersnaam kopiëren";
|
||||
"lng_context_save_image" = "Afbeelding opslaan als...";
|
||||
|
||||
@@ -193,19 +193,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_signin_title" = "Verificar senha";
|
||||
"lng_signin_desc" = "Por favor, insira sua senha.";
|
||||
"lng_signin_recover_desc" = "Por favor, insira o código de seu e-mail\n{email}";
|
||||
"lng_signin_recover_desc" = "Por favor, insira o código de seu email\n{email}";
|
||||
"lng_signin_password" = "Sua senha";
|
||||
"lng_signin_code" = "Código do e-mail";
|
||||
"lng_signin_code" = "Código do email";
|
||||
"lng_signin_recover" = "Esqueceu sua senha?";
|
||||
"lng_signin_recover_title" = "Redefinir senha";
|
||||
"lng_signin_hint" = "Dica: {password_hint}";
|
||||
"lng_signin_recover_hint" = "Código enviado para {recover_email}";
|
||||
"lng_signin_bad_password" = "Você colocou uma senha errada.";
|
||||
"lng_signin_wrong_code" = "Você inseriu um código inválido";
|
||||
"lng_signin_try_password" = "Problemas ao acessar seu e-mail?";
|
||||
"lng_signin_try_password" = "Problemas ao acessar seu email?";
|
||||
"lng_signin_password_removed" = "Sua senha da nuvem foi desativada.\nVocê pode definir uma nova em Configurações.";
|
||||
"lng_signin_no_email_forgot" = "Se você não providenciar um e-mail\nde recuperação ao configurar sua senha, suas opções restantes serão lembrar sua senha ou apagar sua conta.";
|
||||
"lng_signin_cant_email_forgot" = "Se você não pode restaurar o acesso ao e-mail, suas opções restantes serão lembrar sua senha ou apagar sua conta.";
|
||||
"lng_signin_no_email_forgot" = "Se você não providenciar um email\nde recuperação ao configurar sua senha, suas opções restantes serão lembrar sua senha ou apagar sua conta.";
|
||||
"lng_signin_cant_email_forgot" = "Se você não pode restaurar o acesso ao email, suas opções restantes serão lembrar sua senha ou apagar sua conta.";
|
||||
"lng_signin_reset_account" = "Apagar sua conta";
|
||||
"lng_signin_sure_reset" = "Atenção!\n\nVocê perderá todos seus chats e mensagens, juntamente com quaisquer mídias e arquivos!\n\nVocê deseja apagar sua conta?";
|
||||
"lng_signin_reset" = "Apagar";
|
||||
@@ -406,11 +406,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_cloud_password_hint" = "Insira uma dica de senha";
|
||||
"lng_cloud_password_change_hint" = "Insira uma dica de senha";
|
||||
"lng_cloud_password_bad" = "Senha e dica de senha não podem ser iguais.";
|
||||
"lng_cloud_password_email" = "E-mail de recuperação";
|
||||
"lng_cloud_password_bad_email" = "E-mail incorreto, por favor, tente outro.";
|
||||
"lng_cloud_password_email" = "email de recuperação";
|
||||
"lng_cloud_password_bad_email" = "email incorreto, por favor, tente outro.";
|
||||
"lng_cloud_password_about" = "Essa senha será requisitada quando você entrar em um novo dispositivo, após o seu PIN.";
|
||||
"lng_cloud_password_about_recover" = "Atenção! Você tem certeza que não quer\nadicionar um email para recuperação de senha?\n\nSe você esquecer sua senha, perderá\nseu acesso a sua conta no Telegram.";
|
||||
"lng_cloud_password_skip_email" = "Pular e-mail";
|
||||
"lng_cloud_password_skip_email" = "Pular email";
|
||||
"lng_cloud_password_almost" = "Link de confirmação enviado para\no email fornecido. A verificação em duas etapas será ativada assim que você abrir o link.";
|
||||
"lng_cloud_password_was_set" = "Verificação em duas etapas ativada.";
|
||||
"lng_cloud_password_updated" = "Sua senha da nuvem foi atualizada.";
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
387;BA;Bosnia & Herzegovina
|
||||
386;SI;Slovenia
|
||||
385;HR;Croatia
|
||||
383;XK;Kosovo;383 XXXX XXXX;11;
|
||||
382;ME;Montenegro
|
||||
381;RS;Serbia;381 XX XXX XXXX;12;
|
||||
380;UA;Ukraine;380 XX XXX XX XX;12;
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
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;
|
||||
@@ -188,6 +191,7 @@ inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto;
|
||||
inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation;
|
||||
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
|
||||
inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation;
|
||||
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
|
||||
|
||||
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
|
||||
|
||||
@@ -277,6 +281,8 @@ messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDis
|
||||
messageActionScreenshotTaken#4792929b = MessageAction;
|
||||
messageActionCustomAction#fae69f56 message:string = MessageAction;
|
||||
messageActionBotAllowed#abe9affe domain:string = MessageAction;
|
||||
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
|
||||
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
|
||||
|
||||
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
||||
|
||||
@@ -292,7 +298,7 @@ geoPoint#2049d70c long:double lat:double = GeoPoint;
|
||||
|
||||
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
|
||||
|
||||
auth.sentCode#5e002502 flags:# phone_registered:flags.0?true type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
|
||||
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.authorization#cd050916 flags:# tmp_sessions:flags.0?int user:User = auth.Authorization;
|
||||
|
||||
@@ -301,17 +307,10 @@ auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorizat
|
||||
inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
|
||||
inputNotifyUsers#193b4417 = InputNotifyPeer;
|
||||
inputNotifyChats#4a95e84e = InputNotifyPeer;
|
||||
inputNotifyAll#a429b886 = InputNotifyPeer;
|
||||
|
||||
inputPeerNotifyEventsEmpty#f03064d8 = InputPeerNotifyEvents;
|
||||
inputPeerNotifyEventsAll#e86a2c74 = InputPeerNotifyEvents;
|
||||
inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings;
|
||||
|
||||
inputPeerNotifySettings#38935eb2 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = InputPeerNotifySettings;
|
||||
|
||||
peerNotifyEventsEmpty#add53cb3 = PeerNotifyEvents;
|
||||
peerNotifyEventsAll#6d1ded88 = PeerNotifyEvents;
|
||||
|
||||
peerNotifySettings#9acda4c0 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = PeerNotifySettings;
|
||||
peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings;
|
||||
|
||||
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
|
||||
|
||||
@@ -512,7 +511,6 @@ help.support#17c6b5f6 phone_number:string user:User = help.Support;
|
||||
notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
||||
notifyUsers#b4c83b4c = NotifyPeer;
|
||||
notifyChats#c007cec3 = NotifyPeer;
|
||||
notifyAll#74d07c60 = NotifyPeer;
|
||||
|
||||
sendMessageTypingAction#16bf744e = SendMessageAction;
|
||||
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
||||
@@ -565,7 +563,7 @@ documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
|
||||
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
|
||||
|
||||
messages.stickersNotModified#f1749a22 = messages.Stickers;
|
||||
messages.stickers#8a8ecd32 hash:string stickers:Vector<Document> = messages.Stickers;
|
||||
messages.stickers#e4599bbd hash:int stickers:Vector<Document> = messages.Stickers;
|
||||
|
||||
stickerPack#12b299d4 emoticon:string documents:Vector<long> = StickerPack;
|
||||
|
||||
@@ -588,12 +586,12 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s
|
||||
|
||||
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
||||
|
||||
account.noPassword#96dabc18 new_salt:bytes email_unconfirmed_pattern:string = account.Password;
|
||||
account.password#7c18141c current_salt:bytes new_salt:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password;
|
||||
account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password;
|
||||
account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password;
|
||||
|
||||
account.passwordSettings#b7b72ab3 email:string = account.PasswordSettings;
|
||||
account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings;
|
||||
|
||||
account.passwordInputSettings#86916deb flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string = account.PasswordInputSettings;
|
||||
account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings;
|
||||
|
||||
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
||||
|
||||
@@ -681,7 +679,7 @@ channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants;
|
||||
|
||||
channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector<User> = channels.ChannelParticipant;
|
||||
|
||||
help.termsOfService#f1ee3e90 text:string = help.TermsOfService;
|
||||
help.termsOfService#780a0310 flags:# popup:flags.0?true id:DataJSON text:string entities:Vector<MessageEntity> min_age_confirm:flags.1?int = help.TermsOfService;
|
||||
|
||||
foundGif#162ecc1f url:string thumb_url:string content_url:string content_type:string w:int h:int = FoundGif;
|
||||
foundGifCached#9c750409 url:string photo:Photo document:Document = FoundGif;
|
||||
@@ -694,7 +692,7 @@ messages.savedGifs#2e0709a5 hash:int gifs:Vector<Document> = messages.SavedGifs;
|
||||
inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaVenue#aaafadc8 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
|
||||
@@ -706,7 +704,7 @@ inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:Input
|
||||
botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaVenue#4366232e flags:# geo:GeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
|
||||
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
|
||||
@@ -845,6 +843,8 @@ webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vect
|
||||
inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
|
||||
|
||||
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
|
||||
inputWebFileGeoPointLocation#66275a62 geo_point:InputGeoPoint w:int h:int zoom:int scale:int = InputWebFileLocation;
|
||||
inputWebFileGeoMessageLocation#553f32eb peer:InputPeer msg_id:int w:int h:int zoom:int scale:int = InputWebFileLocation;
|
||||
|
||||
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
|
||||
|
||||
@@ -959,15 +959,69 @@ messages.foundStickerSets#5108d648 hash:int sets:Vector<StickerSetCovered> = mes
|
||||
|
||||
fileHash#6242c773 offset:int limit:int hash:bytes = FileHash;
|
||||
|
||||
inputClientProxy#75588b3f address:string port:int = InputClientProxy;
|
||||
|
||||
help.proxyDataEmpty#e09e1fb8 expires:int = help.ProxyData;
|
||||
help.proxyDataPromo#2bf7ee23 expires:int peer:Peer chats:Vector<Chat> users:Vector<User> = help.ProxyData;
|
||||
|
||||
help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
|
||||
help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;
|
||||
|
||||
inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
|
||||
inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
|
||||
|
||||
secureFileEmpty#64199744 = SecureFile;
|
||||
secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
|
||||
|
||||
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
|
||||
|
||||
securePlainPhone#7d6099dd phone:string = SecurePlainData;
|
||||
securePlainEmail#21ec5a5f email:string = SecurePlainData;
|
||||
|
||||
secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
|
||||
secureValueTypePassport#3dac6a00 = SecureValueType;
|
||||
secureValueTypeDriverLicense#6e425c4 = SecureValueType;
|
||||
secureValueTypeIdentityCard#a0d0744b = SecureValueType;
|
||||
secureValueTypeInternalPassport#99a48f23 = SecureValueType;
|
||||
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
||||
secureValueTypeUtilityBill#fc36954e = SecureValueType;
|
||||
secureValueTypeBankStatement#89137c0d = SecureValueType;
|
||||
secureValueTypeRentalAgreement#8b883488 = SecureValueType;
|
||||
secureValueTypePassportRegistration#99e3806a = SecureValueType;
|
||||
secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
|
||||
secureValueTypePhone#b320aadb = SecureValueType;
|
||||
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||
|
||||
secureValue#b4b4b699 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
|
||||
|
||||
inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
|
||||
|
||||
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||
|
||||
secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError;
|
||||
secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
|
||||
|
||||
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
||||
|
||||
account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector<SecureValueType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
|
||||
|
||||
account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode;
|
||||
|
||||
help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo;
|
||||
help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = help.DeepLinkInfo;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
|
||||
initConnection#c7481da6 {X:Type} api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string query:!X = X;
|
||||
initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
|
||||
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
|
||||
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
|
||||
|
||||
auth.checkPhone#6fe51dfb phone_number:string = auth.CheckedPhone;
|
||||
auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode;
|
||||
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
|
||||
auth.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
|
||||
@@ -1015,9 +1069,20 @@ account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPass
|
||||
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
||||
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
||||
account.resetWebAuthorizations#682d2594 = Bool;
|
||||
account.getAllSecureValues#b288bc7d = Vector<SecureValue>;
|
||||
account.getSecureValue#73665bc2 types:Vector<SecureValueType> = Vector<SecureValue>;
|
||||
account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue;
|
||||
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
||||
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
||||
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
|
||||
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
|
||||
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
|
||||
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
|
||||
account.verifyEmail#ecba39db email:string code:string = Bool;
|
||||
|
||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
|
||||
|
||||
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
|
||||
contacts.getContacts#c023849f hash:int = contacts.Contacts;
|
||||
@@ -1070,7 +1135,7 @@ messages.sendEncryptedService#32d439a4 peer:InputEncryptedChat random_id:long da
|
||||
messages.receivedQueue#55a5bb66 max_qts:int = Vector<long>;
|
||||
messages.reportEncryptedSpam#4b0c8c0f peer:InputEncryptedChat = Bool;
|
||||
messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages;
|
||||
messages.getStickers#85cb5182 flags:# exclude_featured:flags.0?true emoticon:string hash:string = messages.Stickers;
|
||||
messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers;
|
||||
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
|
||||
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
|
||||
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite;
|
||||
@@ -1094,8 +1159,8 @@ messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_p
|
||||
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 peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#5d1b8dd flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true peer:InputPeer id:int message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Updates;
|
||||
messages.editInlineBotMessage#b0e08243 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Bool;
|
||||
messages.editMessage#c000e4c8 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Updates;
|
||||
messages.editInlineBotMessage#adc3e828 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = 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;
|
||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||
@@ -1157,10 +1222,13 @@ help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
|
||||
help.getInviteText#4d392343 = help.InviteText;
|
||||
help.getSupport#9cdf08cd = help.Support;
|
||||
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
||||
help.getTermsOfService#350170f3 = help.TermsOfService;
|
||||
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
|
||||
help.getCdnConfig#52029342 = CdnConfig;
|
||||
help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls;
|
||||
help.getProxyData#3d7758e1 = help.ProxyData;
|
||||
help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
|
||||
help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
|
||||
help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
|
||||
|
||||
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
||||
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
||||
@@ -1224,4 +1292,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
|
||||
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
|
||||
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
||||
|
||||
// LAYER 78
|
||||
// LAYER 81
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.2.19.0" />
|
||||
Version="1.3.3.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,19,0
|
||||
PRODUCTVERSION 1,2,19,0
|
||||
FILEVERSION 1,3,3,0
|
||||
PRODUCTVERSION 1,3,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -52,10 +52,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.2.19.0"
|
||||
VALUE "FileVersion", "1.3.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.2.19.0"
|
||||
VALUE "ProductVersion", "1.3.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,19,0
|
||||
PRODUCTVERSION 1,2,19,0
|
||||
FILEVERSION 1,3,3,0
|
||||
PRODUCTVERSION 1,3,3,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.2.19.0"
|
||||
VALUE "FileVersion", "1.3.3.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.2.19.0"
|
||||
VALUE "ProductVersion", "1.3.3.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
extern "C" {
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
@@ -24,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
} // extern "C"
|
||||
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
#include <LzmaLib.h>
|
||||
@@ -34,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
|
||||
using std::string;
|
||||
using std::wstring;
|
||||
using std::cout;
|
||||
using std::cout;
|
||||
|
||||
@@ -254,7 +254,7 @@ int main(int argc, const char * argv[]) {
|
||||
forKey:NSWorkspaceLaunchConfigurationArguments]
|
||||
error:&error];
|
||||
if (!result) {
|
||||
writeLog([@"Could not run application, error: " stringByAppendingString:error ? [error localizedDescription] : @"(nil)"]);
|
||||
writeLog([[NSString stringWithFormat:@"Could not run application, error %ld: ", (long)[error code]] stringByAppendingString: error ? [error localizedDescription] : @"(nil)"]);
|
||||
}
|
||||
closeLog();
|
||||
return result ? 0 : -1;
|
||||
|
||||
@@ -35,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "auth_session.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "window/window_lock_widgets.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
@@ -50,6 +51,8 @@ namespace {
|
||||
constexpr auto kReloadChannelMembersTimeout = 1000; // 1 second wait before reload members in channel after adding
|
||||
constexpr auto kSaveCloudDraftTimeout = 1000; // save draft to the cloud with 1 sec extra delay
|
||||
constexpr auto kSaveDraftBeforeQuitTimeout = 1500; // give the app 1.5 secs to save drafts to cloud when quitting
|
||||
constexpr auto kProxyPromotionInterval = TimeId(60 * 60);
|
||||
constexpr auto kProxyPromotionMinDelay = TimeId(10);
|
||||
constexpr auto kSmallDelayMs = 5;
|
||||
constexpr auto kUnreadMentionsPreloadIfLess = 5;
|
||||
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
||||
@@ -60,6 +63,7 @@ constexpr auto kReadFeaturedSetsTimeout = TimeMs(1000);
|
||||
constexpr auto kFileLoaderQueueStopTimeout = TimeMs(5000);
|
||||
constexpr auto kFeedReadTimeout = TimeMs(1000);
|
||||
constexpr auto kStickersByEmojiInvalidateTimeout = TimeMs(60 * 60 * 1000);
|
||||
constexpr auto kNotifySettingSaveTimeout = TimeMs(1000);
|
||||
|
||||
bool IsSilentPost(not_null<HistoryItem*> item, bool silent) {
|
||||
const auto history = item->history();
|
||||
@@ -126,7 +130,7 @@ FileLoadTo FileLoadTaskOptions(const ApiWrap::SendOptions &options) {
|
||||
const auto peer = options.history->peer;
|
||||
return FileLoadTo(
|
||||
peer->id,
|
||||
peer->notifySilentPosts(),
|
||||
Auth().data().notifySilentPosts(peer),
|
||||
options.replyTo);
|
||||
}
|
||||
|
||||
@@ -134,17 +138,19 @@ FileLoadTo FileLoadTaskOptions(const ApiWrap::SendOptions &options) {
|
||||
|
||||
ApiWrap::ApiWrap(not_null<AuthSession*> session)
|
||||
: _session(session)
|
||||
, _messageDataResolveDelayed([this] { resolveMessageDatas(); })
|
||||
, _webPagesTimer([this] { resolveWebPages(); })
|
||||
, _draftsSaveTimer([this] { saveDraftsToCloud(); })
|
||||
, _featuredSetsReadTimer([this] { readFeaturedSets(); })
|
||||
, _messageDataResolveDelayed([=] { resolveMessageDatas(); })
|
||||
, _webPagesTimer([=] { resolveWebPages(); })
|
||||
, _draftsSaveTimer([=] { saveDraftsToCloud(); })
|
||||
, _featuredSetsReadTimer([=] { readFeaturedSets(); })
|
||||
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout))
|
||||
, _feedReadTimer([this] { readFeeds(); }) {
|
||||
, _feedReadTimer([=] { readFeeds(); })
|
||||
, _proxyPromotionTimer([=] { refreshProxyPromotion(); })
|
||||
, _updateNotifySettingsTimer([=] { sendNotifySettingsUpdates(); }) {
|
||||
}
|
||||
|
||||
void ApiWrap::requestChangelog(
|
||||
const QString &sinceVersion,
|
||||
base::lambda<void(const MTPUpdates &result)> callback) {
|
||||
Fn<void(const MTPUpdates &result)> callback) {
|
||||
request(MTPhelp_GetAppChangelog(
|
||||
MTP_string(sinceVersion)
|
||||
)).done(
|
||||
@@ -152,6 +158,154 @@ void ApiWrap::requestChangelog(
|
||||
).send();
|
||||
}
|
||||
|
||||
void ApiWrap::refreshProxyPromotion() {
|
||||
const auto now = unixtime();
|
||||
const auto next = (_proxyPromotionNextRequestTime != 0)
|
||||
? _proxyPromotionNextRequestTime
|
||||
: now;
|
||||
if (_proxyPromotionRequestId) {
|
||||
getProxyPromotionDelayed(now, next);
|
||||
return;
|
||||
}
|
||||
const auto proxy = Global::UseProxy()
|
||||
? Global::SelectedProxy()
|
||||
: ProxyData();
|
||||
const auto key = [&]() -> std::pair<QString, uint32> {
|
||||
if (!Global::UseProxy()) {
|
||||
return {};
|
||||
}
|
||||
const auto &proxy = Global::SelectedProxy();
|
||||
if (proxy.type != ProxyData::Type::Mtproto) {
|
||||
return {};
|
||||
}
|
||||
return { proxy.host, proxy.port };
|
||||
}();
|
||||
if (_proxyPromotionKey == key && now < next) {
|
||||
getProxyPromotionDelayed(now, next);
|
||||
return;
|
||||
}
|
||||
_proxyPromotionKey = key;
|
||||
if (key.first.isEmpty() || !key.second) {
|
||||
proxyPromotionDone(MTP_help_proxyDataEmpty(
|
||||
MTP_int(unixtime() + kProxyPromotionInterval)));
|
||||
return;
|
||||
}
|
||||
_proxyPromotionRequestId = request(MTPhelp_GetProxyData(
|
||||
)).done([=](const MTPhelp_ProxyData &result) {
|
||||
_proxyPromotionRequestId = 0;
|
||||
proxyPromotionDone(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_proxyPromotionRequestId = 0;
|
||||
const auto now = unixtime();
|
||||
const auto next = _proxyPromotionNextRequestTime = now
|
||||
+ kProxyPromotionInterval;
|
||||
if (!_proxyPromotionTimer.isActive()) {
|
||||
getProxyPromotionDelayed(now, next);
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::getProxyPromotionDelayed(TimeId now, TimeId next) {
|
||||
_proxyPromotionTimer.callOnce(std::min(
|
||||
std::max(next - now, kProxyPromotionMinDelay),
|
||||
kProxyPromotionInterval) * TimeMs(1000));
|
||||
};
|
||||
|
||||
void ApiWrap::proxyPromotionDone(const MTPhelp_ProxyData &proxy) {
|
||||
if (proxy.type() == mtpc_help_proxyDataEmpty) {
|
||||
const auto &data = proxy.c_help_proxyDataEmpty();
|
||||
const auto next = _proxyPromotionNextRequestTime = data.vexpires.v;
|
||||
getProxyPromotionDelayed(unixtime(), next);
|
||||
_session->data().setProxyPromoted(nullptr);
|
||||
return;
|
||||
}
|
||||
Assert(proxy.type() == mtpc_help_proxyDataPromo);
|
||||
const auto &data = proxy.c_help_proxyDataPromo();
|
||||
const auto next = _proxyPromotionNextRequestTime = data.vexpires.v;
|
||||
getProxyPromotionDelayed(unixtime(), next);
|
||||
|
||||
App::feedChats(data.vchats);
|
||||
App::feedUsers(data.vusers);
|
||||
const auto peerId = peerFromMTP(data.vpeer);
|
||||
const auto peer = App::peer(peerId);
|
||||
_session->data().setProxyPromoted(peer);
|
||||
if (const auto history = App::historyLoaded(peer)) {
|
||||
_session->api().requestDialogEntry(history);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::requestDeepLinkInfo(
|
||||
const QString &path,
|
||||
Fn<void(const MTPDhelp_deepLinkInfo &result)> callback) {
|
||||
request(_deepLinkInfoRequestId).cancel();
|
||||
_deepLinkInfoRequestId = request(MTPhelp_GetDeepLinkInfo(
|
||||
MTP_string(path)
|
||||
)).done([=](const MTPhelp_DeepLinkInfo &result) {
|
||||
_deepLinkInfoRequestId = 0;
|
||||
if (result.type() == mtpc_help_deepLinkInfo) {
|
||||
callback(result.c_help_deepLinkInfo());
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_deepLinkInfoRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::requestTermsUpdate() {
|
||||
if (_termsUpdateRequestId) {
|
||||
return;
|
||||
}
|
||||
const auto now = getms(true);
|
||||
if (_termsUpdateSendAt && now < _termsUpdateSendAt) {
|
||||
App::CallDelayed(_termsUpdateSendAt - now, _session, [=] {
|
||||
requestTermsUpdate();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr auto kTermsUpdateTimeoutMin = 10 * TimeMs(1000);
|
||||
constexpr auto kTermsUpdateTimeoutMax = 86400 * TimeMs(1000);
|
||||
|
||||
_termsUpdateRequestId = request(MTPhelp_GetTermsOfServiceUpdate(
|
||||
)).done([=](const MTPhelp_TermsOfServiceUpdate &result) {
|
||||
_termsUpdateRequestId = 0;
|
||||
|
||||
const auto requestNext = [&](auto &&data) {
|
||||
_termsUpdateSendAt = getms(true) + snap(
|
||||
TimeMs(data.vexpires.v - unixtime()),
|
||||
kTermsUpdateTimeoutMin,
|
||||
kTermsUpdateTimeoutMax);
|
||||
requestTermsUpdate();
|
||||
};
|
||||
switch (result.type()) {
|
||||
case mtpc_help_termsOfServiceUpdateEmpty: {
|
||||
const auto &data = result.c_help_termsOfServiceUpdateEmpty();
|
||||
requestNext(data);
|
||||
} break;
|
||||
case mtpc_help_termsOfServiceUpdate: {
|
||||
const auto &data = result.c_help_termsOfServiceUpdate();
|
||||
const auto &terms = data.vterms_of_service;
|
||||
const auto &fields = terms.c_help_termsOfService();
|
||||
Messenger::Instance().lockByTerms(
|
||||
Window::TermsLock::FromMTP(fields));
|
||||
requestNext(data);
|
||||
} break;
|
||||
default: Unexpected("Type in requestTermsUpdate().");
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
_termsUpdateRequestId = 0;
|
||||
_termsUpdateSendAt = getms(true) + kTermsUpdateTimeoutMin;
|
||||
requestTermsUpdate();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::acceptTerms(bytes::const_span id) {
|
||||
request(MTPhelp_AcceptTermsOfService(
|
||||
MTP_dataJSON(MTP_bytes(id))
|
||||
)).done([=](const MTPBool &result) {
|
||||
requestTermsUpdate();
|
||||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::applyUpdates(
|
||||
const MTPUpdates &updates,
|
||||
uint64 sentMessageRandomId) {
|
||||
@@ -179,7 +333,7 @@ void ApiWrap::savePinnedOrder() {
|
||||
//void ApiWrap::toggleChannelGrouping(
|
||||
// not_null<ChannelData*> channel,
|
||||
// bool group,
|
||||
// base::lambda<void()> callback) {
|
||||
// Fn<void()> callback) {
|
||||
// if (const auto already = _channelGroupingRequests.take(channel)) {
|
||||
// request(already->first).cancel();
|
||||
// }
|
||||
@@ -194,7 +348,7 @@ void ApiWrap::savePinnedOrder() {
|
||||
// )).done([=](const MTPUpdates &result) {
|
||||
// applyUpdates(result);
|
||||
// if (group) {
|
||||
// channel->setFeed(Auth().data().feed(feedId));
|
||||
// channel->setFeed(_session->data().feed(feedId));
|
||||
// } else {
|
||||
// channel->clearFeed();
|
||||
// }
|
||||
@@ -463,7 +617,7 @@ void ApiWrap::applyPeerDialogs(const MTPmessages_PeerDialogs &dialogs) {
|
||||
|
||||
//case mtpc_dialogFeed: { // #feed
|
||||
// const auto &fields = dialog.c_dialogFeed();
|
||||
// const auto feed = Auth().data().feed(fields.vfeed_id.v);
|
||||
// const auto feed = _session->data().feed(fields.vfeed_id.v);
|
||||
// feed->applyDialog(fields);
|
||||
//} break;
|
||||
}
|
||||
@@ -1297,7 +1451,7 @@ void ApiWrap::deleteAllFromUser(
|
||||
}
|
||||
}
|
||||
|
||||
Auth().data().sendHistoryChangeNotifications();
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
|
||||
deleteAllFromUserSend(channel, from);
|
||||
}
|
||||
@@ -1314,7 +1468,7 @@ void ApiWrap::deleteAllFromUserSend(
|
||||
deleteAllFromUserSend(channel, from);
|
||||
} else if (const auto history = App::historyLoaded(channel)) {
|
||||
if (!history->lastMessageKnown()) {
|
||||
Auth().api().requestDialogEntry(history);
|
||||
requestDialogEntry(history);
|
||||
}
|
||||
}
|
||||
}).send();
|
||||
@@ -1322,7 +1476,7 @@ void ApiWrap::deleteAllFromUserSend(
|
||||
|
||||
void ApiWrap::requestChannelMembersForAdd(
|
||||
not_null<ChannelData*> channel,
|
||||
base::lambda<void(const MTPchannels_ChannelParticipants&)> callback) {
|
||||
Fn<void(const MTPchannels_ChannelParticipants&)> callback) {
|
||||
_channelMembersForAddCallback = std::move(callback);
|
||||
if (_channelMembersForAdd == channel) {
|
||||
return;
|
||||
@@ -1558,83 +1712,137 @@ void ApiWrap::leaveChannel(not_null<ChannelData*> channel) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::blockUser(UserData *user) {
|
||||
void ApiWrap::blockUser(not_null<UserData*> user) {
|
||||
if (user->isBlocked()) {
|
||||
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserIsBlocked);
|
||||
} else if (!_blockRequests.contains(user)) {
|
||||
} else if (_blockRequests.find(user) == end(_blockRequests)) {
|
||||
auto requestId = request(MTPcontacts_Block(user->inputUser)).done([this, user](const MTPBool &result) {
|
||||
_blockRequests.remove(user);
|
||||
_blockRequests.erase(user);
|
||||
user->setBlockStatus(UserData::BlockStatus::Blocked);
|
||||
}).fail([this, user](const RPCError &error) {
|
||||
_blockRequests.remove(user);
|
||||
_blockRequests.erase(user);
|
||||
}).send();
|
||||
|
||||
_blockRequests.insert(user, requestId);
|
||||
_blockRequests.emplace(user, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::unblockUser(UserData *user) {
|
||||
void ApiWrap::unblockUser(not_null<UserData*> user) {
|
||||
if (!user->isBlocked()) {
|
||||
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserIsBlocked);
|
||||
} else if (!_blockRequests.contains(user)) {
|
||||
} else if (_blockRequests.find(user) == end(_blockRequests)) {
|
||||
auto requestId = request(MTPcontacts_Unblock(user->inputUser)).done([this, user](const MTPBool &result) {
|
||||
_blockRequests.remove(user);
|
||||
_blockRequests.erase(user);
|
||||
user->setBlockStatus(UserData::BlockStatus::NotBlocked);
|
||||
}).fail([this, user](const RPCError &error) {
|
||||
_blockRequests.remove(user);
|
||||
_blockRequests.erase(user);
|
||||
}).send();
|
||||
|
||||
_blockRequests.insert(user, requestId);
|
||||
_blockRequests.emplace(user, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::exportInviteLink(PeerData *peer) {
|
||||
if (_exportInviteRequests.contains(peer)) {
|
||||
void ApiWrap::exportInviteLink(not_null<PeerData*> peer) {
|
||||
if (_exportInviteRequests.find(peer) != end(_exportInviteRequests)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto sendRequest = [this, peer] {
|
||||
auto exportFail = [this, peer](const RPCError &error) {
|
||||
_exportInviteRequests.remove(peer);
|
||||
const auto sendRequest = [this, peer] {
|
||||
const auto exportFail = [this, peer](const RPCError &error) {
|
||||
_exportInviteRequests.erase(peer);
|
||||
};
|
||||
if (auto chat = peer->asChat()) {
|
||||
return request(MTPmessages_ExportChatInvite(chat->inputChat)).done([this, chat](const MTPExportedChatInvite &result) {
|
||||
_exportInviteRequests.remove(chat);
|
||||
chat->setInviteLink((result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString());
|
||||
if (const auto chat = peer->asChat()) {
|
||||
return request(MTPmessages_ExportChatInvite(
|
||||
chat->inputChat
|
||||
)).done([=](const MTPExportedChatInvite &result) {
|
||||
_exportInviteRequests.erase(chat);
|
||||
chat->setInviteLink(
|
||||
(result.type() == mtpc_chatInviteExported
|
||||
? qs(result.c_chatInviteExported().vlink)
|
||||
: QString()));
|
||||
}).fail(exportFail).send();
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
return request(MTPchannels_ExportInvite(channel->inputChannel)).done([this, channel](const MTPExportedChatInvite &result) {
|
||||
_exportInviteRequests.remove(channel);
|
||||
channel->setInviteLink((result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString());
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return request(MTPchannels_ExportInvite(
|
||||
channel->inputChannel
|
||||
)).done([=](const MTPExportedChatInvite &result) {
|
||||
_exportInviteRequests.erase(channel);
|
||||
channel->setInviteLink(
|
||||
(result.type() == mtpc_chatInviteExported
|
||||
? qs(result.c_chatInviteExported().vlink)
|
||||
: QString()));
|
||||
}).fail(exportFail).send();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
if (auto requestId = sendRequest()) {
|
||||
_exportInviteRequests.insert(peer, requestId);
|
||||
if (const auto requestId = sendRequest()) {
|
||||
_exportInviteRequests.emplace(peer, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::requestNotifySetting(PeerData *peer) {
|
||||
if (_notifySettingRequests.contains(peer)) return;
|
||||
|
||||
auto notifyPeer = MTP_inputNotifyPeer(peer->input);
|
||||
auto requestId = request(MTPaccount_GetNotifySettings(notifyPeer)).done([this, notifyPeer, peer](const MTPPeerNotifySettings &result) {
|
||||
notifySettingReceived(notifyPeer, result);
|
||||
_notifySettingRequests.remove(peer);
|
||||
}).fail([this, notifyPeer, peer](const RPCError &error) {
|
||||
notifySettingReceived(notifyPeer, MTP_peerNotifySettings(
|
||||
MTP_flags(MTPDpeerNotifySettings::Flag::f_show_previews),
|
||||
MTP_int(0),
|
||||
MTP_string("default")));
|
||||
_notifySettingRequests.remove(peer);
|
||||
void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) {
|
||||
const auto key = [&] {
|
||||
switch (peer.type()) {
|
||||
case mtpc_inputNotifyUsers: return peerFromUser(0);
|
||||
case mtpc_inputNotifyChats: return peerFromChat(0);
|
||||
case mtpc_inputNotifyPeer: {
|
||||
const auto &inner = peer.c_inputNotifyPeer().vpeer;
|
||||
switch (inner.type()) {
|
||||
case mtpc_inputPeerSelf:
|
||||
return _session->userPeerId();
|
||||
case mtpc_inputPeerEmpty:
|
||||
return PeerId(0);
|
||||
case mtpc_inputPeerChannel:
|
||||
return peerFromChannel(
|
||||
inner.c_inputPeerChannel().vchannel_id);
|
||||
case mtpc_inputPeerChat:
|
||||
return peerFromChat(inner.c_inputPeerChat().vchat_id);
|
||||
case mtpc_inputPeerUser:
|
||||
return peerFromUser(inner.c_inputPeerUser().vuser_id);
|
||||
}
|
||||
Unexpected("Type in ApiRequest::requestNotifySettings peer.");
|
||||
} break;
|
||||
}
|
||||
Unexpected("Type in ApiRequest::requestNotifySettings.");
|
||||
}();
|
||||
if (_notifySettingRequests.find(key) != end(_notifySettingRequests)) {
|
||||
return;
|
||||
}
|
||||
auto requestId = request(MTPaccount_GetNotifySettings(
|
||||
peer
|
||||
)).done([=](const MTPPeerNotifySettings &result) {
|
||||
notifySettingReceived(peer, result);
|
||||
_notifySettingRequests.erase(key);
|
||||
}).fail([=](const RPCError &error) {
|
||||
notifySettingReceived(peer, MTP_peerNotifySettings(
|
||||
MTP_flags(0),
|
||||
MTPBool(),
|
||||
MTPBool(),
|
||||
MTPint(),
|
||||
MTPstring()));
|
||||
_notifySettingRequests.erase(key);
|
||||
}).send();
|
||||
|
||||
_notifySettingRequests.insert(peer, requestId);
|
||||
_notifySettingRequests.emplace(key, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::saveDraftToCloudDelayed(History *history) {
|
||||
_draftsSaveRequestIds.insert(history, 0);
|
||||
void ApiWrap::updateNotifySettingsDelayed(not_null<const PeerData*> peer) {
|
||||
_updateNotifySettingsPeers.emplace(peer);
|
||||
_updateNotifySettingsTimer.callOnce(kNotifySettingSaveTimeout);
|
||||
}
|
||||
|
||||
void ApiWrap::sendNotifySettingsUpdates() {
|
||||
while (!_updateNotifySettingsPeers.empty()) {
|
||||
const auto peer = *_updateNotifySettingsPeers.begin();
|
||||
_updateNotifySettingsPeers.erase(_updateNotifySettingsPeers.begin());
|
||||
request(MTPaccount_UpdateNotifySettings(
|
||||
MTP_inputNotifyPeer(peer->input),
|
||||
peer->notifySerialize()
|
||||
)).afterDelay(_updateNotifySettingsPeers.empty() ? 0 : 10).send();
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::saveDraftToCloudDelayed(not_null<History*> history) {
|
||||
_draftsSaveRequestIds.emplace(history, 0);
|
||||
if (!_draftsSaveTimer.isActive()) {
|
||||
_draftsSaveTimer.callOnce(kSaveCloudDraftTimeout);
|
||||
}
|
||||
@@ -1650,6 +1858,7 @@ void ApiWrap::savePrivacy(const MTPInputPrivacyKey &key, QVector<MTPInputPrivacy
|
||||
|
||||
auto requestId = request(MTPaccount_SetPrivacy(key, MTP_vector<MTPInputPrivacyRule>(std::move(rules)))).done([this, keyTypeId](const MTPaccount_PrivacyRules &result) {
|
||||
Expects(result.type() == mtpc_account_privacyRules);
|
||||
|
||||
auto &rules = result.c_account_privacyRules();
|
||||
App::feedUsers(rules.vusers);
|
||||
_privacySaveRequests.remove(keyTypeId);
|
||||
@@ -1825,9 +2034,9 @@ void ApiWrap::applyAffectedMessages(
|
||||
|
||||
void ApiWrap::saveDraftsToCloud() {
|
||||
for (auto i = _draftsSaveRequestIds.begin(), e = _draftsSaveRequestIds.end(); i != e; ++i) {
|
||||
if (i.value()) continue; // sent already
|
||||
if (i->second) continue; // sent already
|
||||
|
||||
auto history = i.key();
|
||||
auto history = i->first;
|
||||
auto cloudDraft = history->cloudDraft();
|
||||
auto localDraft = history->localDraft();
|
||||
if (cloudDraft && cloudDraft->saveRequestId) {
|
||||
@@ -1856,8 +2065,8 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
}
|
||||
}
|
||||
auto i = _draftsSaveRequestIds.find(history);
|
||||
if (i != _draftsSaveRequestIds.cend() && i.value() == requestId) {
|
||||
_draftsSaveRequestIds.remove(history);
|
||||
if (i != _draftsSaveRequestIds.cend() && i->second == requestId) {
|
||||
_draftsSaveRequestIds.erase(history);
|
||||
checkQuitPreventFinished();
|
||||
}
|
||||
}).fail([this, history](const RPCError &error, mtpRequestId requestId) {
|
||||
@@ -1867,18 +2076,18 @@ void ApiWrap::saveDraftsToCloud() {
|
||||
}
|
||||
}
|
||||
auto i = _draftsSaveRequestIds.find(history);
|
||||
if (i != _draftsSaveRequestIds.cend() && i.value() == requestId) {
|
||||
_draftsSaveRequestIds.remove(history);
|
||||
if (i != _draftsSaveRequestIds.cend() && i->second == requestId) {
|
||||
_draftsSaveRequestIds.erase(history);
|
||||
checkQuitPreventFinished();
|
||||
}
|
||||
}).send();
|
||||
|
||||
i.value() = cloudDraft->saveRequestId;
|
||||
i->second = cloudDraft->saveRequestId;
|
||||
}
|
||||
}
|
||||
|
||||
bool ApiWrap::isQuitPrevent() {
|
||||
if (_draftsSaveRequestIds.isEmpty()) {
|
||||
if (_draftsSaveRequestIds.empty()) {
|
||||
return false;
|
||||
}
|
||||
LOG(("ApiWrap prevents quit, saving drafts..."));
|
||||
@@ -1887,7 +2096,7 @@ bool ApiWrap::isQuitPrevent() {
|
||||
}
|
||||
|
||||
void ApiWrap::checkQuitPreventFinished() {
|
||||
if (_draftsSaveRequestIds.isEmpty()) {
|
||||
if (_draftsSaveRequestIds.empty()) {
|
||||
if (App::quitting()) {
|
||||
LOG(("ApiWrap doesn't prevent quit any more."));
|
||||
}
|
||||
@@ -1895,41 +2104,43 @@ void ApiWrap::checkQuitPreventFinished() {
|
||||
}
|
||||
}
|
||||
|
||||
PeerData *ApiWrap::notifySettingReceived(
|
||||
void ApiWrap::notifySettingReceived(
|
||||
MTPInputNotifyPeer notifyPeer,
|
||||
const MTPPeerNotifySettings &settings) {
|
||||
PeerData *requestedPeer = nullptr;
|
||||
switch (notifyPeer.type()) {
|
||||
case mtpc_inputNotifyAll:
|
||||
App::main()->applyNotifySetting(MTP_notifyAll(), settings);
|
||||
break;
|
||||
case mtpc_inputNotifyUsers:
|
||||
App::main()->applyNotifySetting(MTP_notifyUsers(), settings);
|
||||
_session->data().applyNotifySetting(MTP_notifyUsers(), settings);
|
||||
break;
|
||||
case mtpc_inputNotifyChats:
|
||||
App::main()->applyNotifySetting(MTP_notifyChats(), settings);
|
||||
_session->data().applyNotifySetting(MTP_notifyChats(), settings);
|
||||
break;
|
||||
case mtpc_inputNotifyPeer: {
|
||||
auto &peer = notifyPeer.c_inputNotifyPeer().vpeer;
|
||||
switch (peer.type()) {
|
||||
case mtpc_inputPeerEmpty: App::main()->applyNotifySetting(
|
||||
MTP_notifyPeer(MTP_peerUser(MTP_int(0))),
|
||||
settings);
|
||||
break;
|
||||
case mtpc_inputPeerSelf: requestedPeer = App::self(); break;
|
||||
case mtpc_inputPeerUser: requestedPeer = App::user(peerFromUser(peer.c_inputPeerUser().vuser_id)); break;
|
||||
case mtpc_inputPeerChat: requestedPeer = App::chat(peerFromChat(peer.c_inputPeerChat().vchat_id)); break;
|
||||
case mtpc_inputPeerChannel: requestedPeer = App::channel(peerFromChannel(peer.c_inputPeerChannel().vchannel_id)); break;
|
||||
}
|
||||
if (requestedPeer) {
|
||||
App::main()->applyNotifySetting(
|
||||
MTP_notifyPeer(peerToMTP(requestedPeer->id)),
|
||||
const auto apply = [&](PeerId peerId) {
|
||||
_session->data().applyNotifySetting(
|
||||
MTP_notifyPeer(peerToMTP(peerId)),
|
||||
settings);
|
||||
};
|
||||
switch (peer.type()) {
|
||||
case mtpc_inputPeerEmpty:
|
||||
apply(0);
|
||||
break;
|
||||
case mtpc_inputPeerSelf:
|
||||
apply(_session->userPeerId());
|
||||
break;
|
||||
case mtpc_inputPeerUser:
|
||||
apply(peerFromUser(peer.c_inputPeerUser().vuser_id));
|
||||
break;
|
||||
case mtpc_inputPeerChat:
|
||||
apply(peerFromChat(peer.c_inputPeerChat().vchat_id));
|
||||
break;
|
||||
case mtpc_inputPeerChannel:
|
||||
apply(peerFromChannel(peer.c_inputPeerChannel().vchannel_id));
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
_session->notifications().checkDelayed();
|
||||
return requestedPeer;
|
||||
}
|
||||
|
||||
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
|
||||
@@ -2246,11 +2457,10 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
|
||||
if (sendRequest) {
|
||||
const auto hash = (it != _stickersByEmoji.end())
|
||||
? it->second.hash
|
||||
: QString();
|
||||
: int32(0);
|
||||
request(MTPmessages_GetStickers(
|
||||
MTP_flags(MTPmessages_GetStickers::Flag::f_exclude_featured),
|
||||
MTP_string(emoji->text()),
|
||||
MTP_string(hash)
|
||||
MTP_int(hash)
|
||||
)).done([=](const MTPmessages_Stickers &result) {
|
||||
if (result.type() == mtpc_messages_stickersNotModified) {
|
||||
return;
|
||||
@@ -2260,13 +2470,7 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
|
||||
auto &entry = _stickersByEmoji[emoji];
|
||||
entry.list.clear();
|
||||
entry.list.reserve(data.vstickers.v.size());
|
||||
for (const auto &sticker : data.vstickers.v) {
|
||||
const auto document = Auth().data().document(sticker);
|
||||
if (document->sticker()) {
|
||||
entry.list.push_back(document);
|
||||
}
|
||||
}
|
||||
entry.hash = qs(data.vhash);
|
||||
entry.hash = data.vhash.v;
|
||||
entry.received = getms(true);
|
||||
_session->data().notifyStickersUpdated();
|
||||
}).send();
|
||||
@@ -2470,10 +2674,10 @@ void ApiWrap::readFeaturedSets() {
|
||||
void ApiWrap::parseChannelParticipants(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPchannels_ChannelParticipants &result,
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list)> callbackList,
|
||||
base::lambda<void()> callbackNotModified) {
|
||||
Fn<void()> callbackNotModified) {
|
||||
TLHelp::VisitChannelParticipants(result, base::overload([&](
|
||||
const MTPDchannels_channelParticipants &data) {
|
||||
App::feedUsers(data.vusers);
|
||||
@@ -2507,10 +2711,10 @@ void ApiWrap::refreshChannelAdmins(
|
||||
void ApiWrap::parseRecentChannelParticipants(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPchannels_ChannelParticipants &result,
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list)> callbackList,
|
||||
base::lambda<void()> callbackNotModified) {
|
||||
Fn<void()> callbackNotModified) {
|
||||
parseChannelParticipants(channel, result, [&](
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list) {
|
||||
@@ -3389,12 +3593,12 @@ void ApiWrap::sendAction(const SendOptions &options) {
|
||||
void ApiWrap::forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
const SendOptions &options,
|
||||
base::lambda_once<void()> &&successCallback) {
|
||||
FnMut<void()> &&successCallback) {
|
||||
Expects(!items.empty());
|
||||
|
||||
struct SharedCallback {
|
||||
int requestsLeft = 0;
|
||||
base::lambda_once<void()> callback;
|
||||
FnMut<void()> callback;
|
||||
};
|
||||
const auto shared = successCallback
|
||||
? std::make_shared<SharedCallback>()
|
||||
@@ -3411,7 +3615,8 @@ void ApiWrap::forwardMessages(
|
||||
readServerHistory(history);
|
||||
|
||||
const auto channelPost = peer->isChannel() && !peer->isMegagroup();
|
||||
const auto silentPost = channelPost && peer->notifySilentPosts();
|
||||
const auto silentPost = channelPost
|
||||
&& _session->data().notifySilentPosts(peer);
|
||||
|
||||
auto flags = MTPDmessage::Flags(0);
|
||||
auto sendFlags = MTPmessages_ForwardMessages::Flags(0);
|
||||
@@ -3586,7 +3791,7 @@ void ApiWrap::sendSharedContact(
|
||||
MTP_string(phone),
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName));
|
||||
sendMedia(item, media, peer->notifySilentPosts());
|
||||
sendMedia(item, media, _session->data().notifySilentPosts(peer));
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
@@ -3600,7 +3805,7 @@ void ApiWrap::sendVoiceMessage(
|
||||
VoiceWaveform waveform,
|
||||
int duration,
|
||||
const SendOptions &options) {
|
||||
const auto caption = QString();
|
||||
const auto caption = TextWithTags();
|
||||
const auto to = FileLoadTaskOptions(options);
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
result,
|
||||
@@ -3613,16 +3818,16 @@ void ApiWrap::sendVoiceMessage(
|
||||
void ApiWrap::sendFiles(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
QString caption,
|
||||
TextWithTags &&caption,
|
||||
std::shared_ptr<SendingAlbum> album,
|
||||
const SendOptions &options) {
|
||||
if (list.files.size() > 1 && !caption.isEmpty()) {
|
||||
if (list.files.size() > 1 && !caption.text.isEmpty()) {
|
||||
auto message = MainWidget::MessageToSend(options.history);
|
||||
message.textWithTags = { caption, TextWithTags::Tags() };
|
||||
message.textWithTags = std::move(caption);
|
||||
message.replyTo = options.replyTo;
|
||||
message.clearDraft = false;
|
||||
App::main()->sendMessage(message);
|
||||
caption = QString();
|
||||
caption = TextWithTags();
|
||||
}
|
||||
|
||||
const auto to = FileLoadTaskOptions(options);
|
||||
@@ -3667,7 +3872,7 @@ void ApiWrap::sendFile(
|
||||
SendMediaType type,
|
||||
const SendOptions &options) {
|
||||
auto to = FileLoadTaskOptions(options);
|
||||
auto caption = QString();
|
||||
auto caption = TextWithTags();
|
||||
_fileLoader->addTask(std::make_unique<FileLoadTask>(
|
||||
QString(),
|
||||
fileContent,
|
||||
|
||||
@@ -63,10 +63,10 @@ public:
|
||||
//void toggleChannelGrouping( // #feed
|
||||
// not_null<ChannelData*> channel,
|
||||
// bool group,
|
||||
// base::lambda<void()> callback);
|
||||
// Fn<void()> callback);
|
||||
//void ungroupAllFromFeed(not_null<Data::Feed*> feed);
|
||||
|
||||
using RequestMessageDataCallback = base::lambda<void(ChannelData*, MsgId)>;
|
||||
using RequestMessageDataCallback = Fn<void(ChannelData*, MsgId)>;
|
||||
void requestMessageData(
|
||||
ChannelData *channel,
|
||||
MsgId msgId,
|
||||
@@ -92,11 +92,17 @@ public:
|
||||
|
||||
void requestChangelog(
|
||||
const QString &sinceVersion,
|
||||
base::lambda<void(const MTPUpdates &result)> callback);
|
||||
Fn<void(const MTPUpdates &result)> callback);
|
||||
void refreshProxyPromotion();
|
||||
void requestDeepLinkInfo(
|
||||
const QString &path,
|
||||
Fn<void(const MTPDhelp_deepLinkInfo &result)> callback);
|
||||
void requestTermsUpdate();
|
||||
void acceptTerms(bytes::const_span termsId);
|
||||
|
||||
void requestChannelMembersForAdd(
|
||||
not_null<ChannelData*> channel,
|
||||
base::lambda<void(const MTPchannels_ChannelParticipants&)> callback);
|
||||
Fn<void(const MTPchannels_ChannelParticipants&)> callback);
|
||||
void processFullPeer(PeerData *peer, const MTPmessages_ChatFull &result);
|
||||
void processFullPeer(UserData *user, const MTPUserFull &result);
|
||||
|
||||
@@ -136,13 +142,13 @@ public:
|
||||
void joinChannel(not_null<ChannelData*> channel);
|
||||
void leaveChannel(not_null<ChannelData*> channel);
|
||||
|
||||
void blockUser(UserData *user);
|
||||
void unblockUser(UserData *user);
|
||||
void blockUser(not_null<UserData*> user);
|
||||
void unblockUser(not_null<UserData*> user);
|
||||
|
||||
void exportInviteLink(PeerData *peer);
|
||||
void requestNotifySetting(PeerData *peer);
|
||||
|
||||
void saveDraftToCloudDelayed(History *history);
|
||||
void exportInviteLink(not_null<PeerData*> peer);
|
||||
void requestNotifySettings(const MTPInputNotifyPeer &peer);
|
||||
void updateNotifySettingsDelayed(not_null<const PeerData*> peer);
|
||||
void saveDraftToCloudDelayed(not_null<History*> history);
|
||||
|
||||
void savePrivacy(const MTPInputPrivacyKey &key, QVector<MTPInputPrivacyRule> &&rules);
|
||||
void handlePrivacyChange(mtpTypeId keyTypeId, const MTPVector<MTPPrivacyRule> &rules);
|
||||
@@ -202,17 +208,17 @@ public:
|
||||
void parseChannelParticipants(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPchannels_ChannelParticipants &result,
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list)> callbackList,
|
||||
base::lambda<void()> callbackNotModified = nullptr);
|
||||
Fn<void()> callbackNotModified = nullptr);
|
||||
void parseRecentChannelParticipants(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPchannels_ChannelParticipants &result,
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list)> callbackList,
|
||||
base::lambda<void()> callbackNotModified = nullptr);
|
||||
Fn<void()> callbackNotModified = nullptr);
|
||||
|
||||
struct SendOptions {
|
||||
SendOptions(not_null<History*> history) : history(history) {
|
||||
@@ -231,7 +237,7 @@ public:
|
||||
void forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
const SendOptions &options,
|
||||
base::lambda_once<void()> &&successCallback = nullptr);
|
||||
FnMut<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
@@ -252,7 +258,7 @@ public:
|
||||
void sendFiles(
|
||||
Storage::PreparedList &&list,
|
||||
SendMediaType type,
|
||||
QString caption,
|
||||
TextWithTags &&caption,
|
||||
std::shared_ptr<SendingAlbum> album,
|
||||
const SendOptions &options);
|
||||
void sendFile(
|
||||
@@ -284,7 +290,7 @@ private:
|
||||
|
||||
struct StickersByEmoji {
|
||||
std::vector<not_null<DocumentData*>> list;
|
||||
QString hash;
|
||||
int32 hash = 0;
|
||||
TimeMs received = 0;
|
||||
};
|
||||
|
||||
@@ -337,7 +343,7 @@ private:
|
||||
MsgRange range,
|
||||
const MTPupdates_ChannelDifference &result);
|
||||
|
||||
PeerData *notifySettingReceived(
|
||||
void notifySettingReceived(
|
||||
MTPInputNotifyPeer peer,
|
||||
const MTPPeerNotifySettings &settings);
|
||||
|
||||
@@ -359,7 +365,6 @@ private:
|
||||
not_null<ChannelData*> channel,
|
||||
const QVector<MTPChannelParticipant> &participants);
|
||||
|
||||
|
||||
void jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date);
|
||||
void jumpToFeedDate(not_null<Data::Feed*> feed, const QDate &date);
|
||||
template <typename Callback>
|
||||
@@ -437,6 +442,11 @@ private:
|
||||
|
||||
void readFeeds();
|
||||
|
||||
void getProxyPromotionDelayed(TimeId now, TimeId next);
|
||||
void proxyPromotionDone(const MTPhelp_ProxyData &proxy);
|
||||
|
||||
void sendNotifySettingsUpdates();
|
||||
|
||||
not_null<AuthSession*> _session;
|
||||
|
||||
MessageDataRequests _messageDataRequests;
|
||||
@@ -454,11 +464,11 @@ private:
|
||||
|
||||
ChannelData *_channelMembersForAdd = nullptr;
|
||||
mtpRequestId _channelMembersForAddRequestId = 0;
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback;
|
||||
base::flat_map<
|
||||
not_null<ChannelData*>,
|
||||
std::pair<mtpRequestId,base::lambda<void()>>> _channelGroupingRequests;
|
||||
std::pair<mtpRequestId,Fn<void()>>> _channelGroupingRequests;
|
||||
|
||||
using KickRequest = std::pair<
|
||||
not_null<ChannelData*>,
|
||||
@@ -477,12 +487,10 @@ private:
|
||||
QMap<uint64, QPair<uint64, mtpRequestId> > _stickerSetRequests;
|
||||
|
||||
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
|
||||
QMap<UserData*, mtpRequestId> _blockRequests;
|
||||
QMap<PeerData*, mtpRequestId> _exportInviteRequests;
|
||||
|
||||
QMap<PeerData*, mtpRequestId> _notifySettingRequests;
|
||||
|
||||
QMap<History*, mtpRequestId> _draftsSaveRequestIds;
|
||||
base::flat_map<not_null<UserData*>, mtpRequestId> _blockRequests;
|
||||
base::flat_map<not_null<PeerData*>, mtpRequestId> _exportInviteRequests;
|
||||
base::flat_map<PeerId, mtpRequestId> _notifySettingRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _draftsSaveRequestIds;
|
||||
base::Timer _draftsSaveTimer;
|
||||
|
||||
base::flat_set<mtpRequestId> _stickerSetDisenableRequests;
|
||||
@@ -567,4 +575,17 @@ private:
|
||||
base::flat_map<not_null<Data::Feed*>, mtpRequestId> _feedReadRequests;
|
||||
base::Timer _feedReadTimer;
|
||||
|
||||
mtpRequestId _proxyPromotionRequestId = 0;
|
||||
std::pair<QString, uint32> _proxyPromotionKey;
|
||||
TimeId _proxyPromotionNextRequestTime = TimeId(0);
|
||||
base::Timer _proxyPromotionTimer;
|
||||
|
||||
base::flat_set<not_null<const PeerData*>> _updateNotifySettingsPeers;
|
||||
base::Timer _updateNotifySettingsTimer;
|
||||
|
||||
mtpRequestId _deepLinkInfoRequestId = 0;
|
||||
|
||||
TimeMs _termsUpdateSendAt = 0;
|
||||
mtpRequestId _termsUpdateRequestId = 0;
|
||||
|
||||
};
|
||||
|
||||
@@ -53,9 +53,6 @@ namespace {
|
||||
using PeersData = QHash<PeerId, PeerData*>;
|
||||
PeersData peersData;
|
||||
|
||||
using MutedPeers = QMap<not_null<PeerData*>, bool>;
|
||||
MutedPeers mutedPeers;
|
||||
|
||||
using LocationsData = QHash<LocationCoords, LocationData*>;
|
||||
LocationsData locationsData;
|
||||
|
||||
@@ -143,50 +140,6 @@ namespace App {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool passcoded() {
|
||||
if (auto window = wnd()) {
|
||||
return window->passcodeWidget();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool loggedOut() {
|
||||
if (Global::LocalPasscode()) {
|
||||
Global::SetLocalPasscode(false);
|
||||
Global::RefLocalPasscodeChanged().notify();
|
||||
}
|
||||
Media::Player::mixer()->stopAndClear();
|
||||
if (auto w = wnd()) {
|
||||
w->tempDirDelete(Local::ClearManagerAll);
|
||||
w->setupIntro();
|
||||
}
|
||||
histories().clear();
|
||||
Messenger::Instance().authSessionDestroy();
|
||||
Local::reset();
|
||||
Window::Theme::Background()->reset();
|
||||
|
||||
cSetOtherOnline(0);
|
||||
clearStorageImages();
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void logOut() {
|
||||
if (auto mtproto = Messenger::Instance().mtp()) {
|
||||
mtproto->logout(rpcDone([] {
|
||||
return loggedOut();
|
||||
}), rpcFail([] {
|
||||
return loggedOut();
|
||||
}));
|
||||
} else {
|
||||
// We log out because we've forgotten passcode.
|
||||
// So we just start mtproto from scratch.
|
||||
Messenger::Instance().startMtp();
|
||||
loggedOut();
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// we should get a full restriction in "{fulltype}: {reason}" format and we
|
||||
// need to find a "-all" tag in {fulltype}, otherwise ignore this restriction
|
||||
@@ -718,7 +671,7 @@ namespace {
|
||||
auto h = App::historyLoaded(chat->id);
|
||||
bool found = !h || !h->lastKeyboardFrom;
|
||||
auto botStatus = -1;
|
||||
for (auto i = chat->participants.begin(), e = chat->participants.end(); i != e;) {
|
||||
for (auto i = chat->participants.begin(); i != chat->participants.end();) {
|
||||
auto [user, version] = *i;
|
||||
if (version < pversion) {
|
||||
i = chat->participants.erase(i);
|
||||
@@ -1153,14 +1106,23 @@ namespace {
|
||||
return i.value();
|
||||
}
|
||||
|
||||
void enumerateUsers(base::lambda<void(UserData*)> action) {
|
||||
for_const (auto peer, peersData) {
|
||||
if (auto user = peer->asUser()) {
|
||||
void enumerateUsers(Fn<void(not_null<UserData*>)> action) {
|
||||
for_const (const auto peer, peersData) {
|
||||
if (const auto user = peer->asUser()) {
|
||||
action(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enumerateChatsChannels(
|
||||
Fn<void(not_null<PeerData*>)> action) {
|
||||
for_const (const auto peer, peersData) {
|
||||
if (!peer->isUser()) {
|
||||
action(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UserData *self() {
|
||||
return ::self;
|
||||
}
|
||||
@@ -1309,7 +1271,6 @@ namespace {
|
||||
void historyClearItems() {
|
||||
randomData.clear();
|
||||
sentData.clear();
|
||||
mutedPeers.clear();
|
||||
cSetSavedPeers(SavedPeers());
|
||||
cSetSavedPeersByTime(SavedPeersByTime());
|
||||
cSetRecentInlineBots(RecentInlineBots());
|
||||
@@ -1748,38 +1709,6 @@ namespace {
|
||||
return QPixmap::fromImage(std::move(image), Qt::ColorOnly);
|
||||
}
|
||||
|
||||
void regMuted(not_null<PeerData*> peer, TimeMs changeIn) {
|
||||
::mutedPeers.insert(peer, true);
|
||||
App::main()->updateMutedIn(changeIn);
|
||||
}
|
||||
|
||||
void unregMuted(not_null<PeerData*> peer) {
|
||||
::mutedPeers.remove(peer);
|
||||
}
|
||||
|
||||
void updateMuted() {
|
||||
auto changeInMin = TimeMs(0);
|
||||
for (auto i = ::mutedPeers.begin(); i != ::mutedPeers.end();) {
|
||||
const auto history = App::historyLoaded(i.key()->id);
|
||||
const auto muteFinishesIn = i.key()->notifyMuteFinishesIn();
|
||||
if (muteFinishesIn > 0) {
|
||||
if (history) {
|
||||
history->changeMute(true);
|
||||
}
|
||||
if (!changeInMin || muteFinishesIn < changeInMin) {
|
||||
changeInMin = muteFinishesIn;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
if (history) {
|
||||
history->changeMute(false);
|
||||
}
|
||||
i = ::mutedPeers.erase(i);
|
||||
}
|
||||
}
|
||||
if (changeInMin) App::main()->updateMutedIn(changeInMin);
|
||||
}
|
||||
|
||||
void rectWithCorners(Painter &p, QRect rect, const style::color &bg, RoundCorners index, RectParts corners) {
|
||||
auto parts = RectPart::Top
|
||||
| RectPart::NoTopBottom
|
||||
|
||||
@@ -66,9 +66,6 @@ enum RoundCorners {
|
||||
namespace App {
|
||||
MainWindow *wnd();
|
||||
MainWidget *main();
|
||||
bool passcoded();
|
||||
|
||||
void logOut();
|
||||
|
||||
QString formatPhone(QString phone);
|
||||
|
||||
@@ -135,7 +132,9 @@ namespace App {
|
||||
inline ChannelData *channelLoaded(ChannelId channelId) {
|
||||
return channel(channelId, PeerData::FullLoaded);
|
||||
}
|
||||
void enumerateUsers(base::lambda<void(UserData*)> action);
|
||||
void enumerateUsers(Fn<void(not_null<UserData*>)> action);
|
||||
void enumerateChatsChannels(
|
||||
Fn<void(not_null<PeerData*>)> action);
|
||||
|
||||
UserData *self();
|
||||
PeerData *peerByName(const QString &username);
|
||||
@@ -221,10 +220,6 @@ namespace App {
|
||||
QImage readImage(const QString &file, QByteArray *format = nullptr, bool opaque = true, bool *animated = nullptr, QByteArray *content = 0);
|
||||
QPixmap pixmapFromImageInPlace(QImage &&image);
|
||||
|
||||
void regMuted(not_null<PeerData*> peer, TimeMs changeIn);
|
||||
void unregMuted(not_null<PeerData*> peer);
|
||||
void updateMuted();
|
||||
|
||||
void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
|
||||
void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
|
||||
|
||||
|
||||
@@ -15,11 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "core/crash_reports.h"
|
||||
#include "messenger.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/crash_report_window.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kEmptyPidForCommandResponse = 0ULL;
|
||||
|
||||
QChar _toHex(ushort v) {
|
||||
v = v & 0x000F;
|
||||
return QChar::fromLatin1((v >= 10) ? ('a' + (v - 10)) : ('0' + v));
|
||||
@@ -60,6 +64,27 @@ QString _escapeFrom7bit(const QString &str) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool StartUrlRequiresActivate(const QString &url) {
|
||||
const auto urlTrimmed = url.trimmed();
|
||||
if (!urlTrimmed.startsWith(qstr("tg://"), Qt::CaseInsensitive)
|
||||
|| Messenger::Instance().locked()) {
|
||||
return true;
|
||||
}
|
||||
const auto command = urlTrimmed.midRef(qstr("tg://").size());
|
||||
|
||||
using namespace qthelp;
|
||||
const auto matchOptions = RegExOption::CaseInsensitive;
|
||||
const auto authMatch = regex_match(
|
||||
qsl("^passport/?\\?(.+)(#|$)"),
|
||||
command,
|
||||
matchOptions);
|
||||
const auto authLegacyMatch = regex_match(
|
||||
qsl("^resolve/?\\?domain=telegrampassport&(.+)(#|$)"),
|
||||
command,
|
||||
matchOptions);
|
||||
return !authMatch->hasMatch() && !authLegacyMatch->hasMatch();
|
||||
}
|
||||
|
||||
Application::Application(
|
||||
not_null<Core::Launcher*> launcher,
|
||||
int &argc,
|
||||
@@ -119,8 +144,9 @@ void Application::socketConnected() {
|
||||
}
|
||||
if (!cStartUrl().isEmpty()) {
|
||||
commands += qsl("OPEN:") + _escapeTo7bit(cStartUrl()) + ';';
|
||||
} else {
|
||||
commands += qsl("CMD:show;");
|
||||
}
|
||||
commands += qsl("CMD:show;");
|
||||
|
||||
DEBUG_LOG(("Application Info: writing commands %1").arg(commands));
|
||||
_localSocket.write(commands.toLatin1());
|
||||
@@ -145,7 +171,9 @@ void Application::socketReading() {
|
||||
_localSocketReadData.append(_localSocket.readAll());
|
||||
if (QRegularExpression("RES:(\\d+);").match(_localSocketReadData).hasMatch()) {
|
||||
uint64 pid = _localSocketReadData.mid(4, _localSocketReadData.length() - 5).toULongLong();
|
||||
psActivateProcess(pid);
|
||||
if (pid != kEmptyPidForCommandResponse) {
|
||||
psActivateProcess(pid);
|
||||
}
|
||||
LOG(("Show command response received, pid = %1, activating and quitting...").arg(pid));
|
||||
return App::quit();
|
||||
}
|
||||
@@ -246,16 +274,26 @@ void Application::readClients() {
|
||||
QStringRef cmd(&cmds, from, to - from);
|
||||
if (cmd.startsWith(qsl("CMD:"))) {
|
||||
Sandbox::execExternal(cmds.mid(from + 4, to - from - 4));
|
||||
QByteArray response(qsl("RES:%1;").arg(QCoreApplication::applicationPid()).toLatin1());
|
||||
const auto response = qsl("RES:%1;").arg(QCoreApplication::applicationPid()).toLatin1();
|
||||
i->first->write(response.data(), response.size());
|
||||
} else if (cmd.startsWith(qsl("SEND:"))) {
|
||||
if (cSendPaths().isEmpty()) {
|
||||
toSend.append(_escapeFrom7bit(cmds.mid(from + 5, to - from - 5)));
|
||||
}
|
||||
} else if (cmd.startsWith(qsl("OPEN:"))) {
|
||||
auto activateRequired = true;
|
||||
if (cStartUrl().isEmpty()) {
|
||||
startUrl = _escapeFrom7bit(cmds.mid(from + 5, to - from - 5)).mid(0, 8192);
|
||||
activateRequired = StartUrlRequiresActivate(startUrl);
|
||||
}
|
||||
if (activateRequired) {
|
||||
Sandbox::execExternal("show");
|
||||
}
|
||||
const auto responsePid = activateRequired
|
||||
? QCoreApplication::applicationPid()
|
||||
: kEmptyPidForCommandResponse;
|
||||
const auto response = qsl("RES:%1;").arg(responsePid).toLatin1();
|
||||
i->first->write(response.data(), response.size());
|
||||
} else {
|
||||
LOG(("Application Error: unknown command %1 passed in local socket").arg(QString(cmd.constData(), cmd.length())));
|
||||
}
|
||||
@@ -321,7 +359,8 @@ void Application::refreshGlobalProxy() {
|
||||
}();
|
||||
if (proxy.type == ProxyData::Type::Socks5
|
||||
|| proxy.type == ProxyData::Type::Http) {
|
||||
QNetworkProxy::setApplicationProxy(ToNetworkProxy(proxy));
|
||||
QNetworkProxy::setApplicationProxy(
|
||||
ToNetworkProxy(ToDirectIpProxy(proxy)));
|
||||
} else {
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ class Launcher;
|
||||
class UpdateChecker;
|
||||
} // namespace Core
|
||||
|
||||
bool StartUrlRequiresActivate(const QString &url);
|
||||
|
||||
class Application : public QApplication {
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -281,10 +281,20 @@ AuthSession::AuthSession(UserId userId)
|
||||
_saveDataTimer.setCallback([=] {
|
||||
Local::writeUserSettings();
|
||||
});
|
||||
subscribe(Messenger::Instance().passcodedChanged(), [=] {
|
||||
Messenger::Instance().passcodeLockChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
_shouldLockAt = 0;
|
||||
}, _lifetime);
|
||||
Messenger::Instance().lockChanges(
|
||||
) | rpl::start_with_next([=] {
|
||||
notifications().updateAll();
|
||||
}, _lifetime);
|
||||
subscribe(Global::RefConnectionTypeChanged(), [=] {
|
||||
_api->refreshProxyPromotion();
|
||||
});
|
||||
_api->refreshProxyPromotion();
|
||||
_api->requestTermsUpdate();
|
||||
|
||||
Window::Theme::Background()->start();
|
||||
}
|
||||
|
||||
@@ -304,9 +314,12 @@ base::Observable<void> &AuthSession::downloaderTaskFinished() {
|
||||
}
|
||||
|
||||
bool AuthSession::validateSelf(const MTPUser &user) {
|
||||
if (user.type() != mtpc_user || !user.c_user().is_self() || user.c_user().vid.v != userId()) {
|
||||
if (user.type() != mtpc_user || !user.c_user().is_self()) {
|
||||
LOG(("API Error: bad self user received."));
|
||||
return false;
|
||||
} else if (user.c_user().vid.v != userId()) {
|
||||
LOG(("Auth Error: wrong self user received."));
|
||||
App::logOutDelayed();
|
||||
crl::on_main(this, [] { Messenger::Instance().logOut(); });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -314,11 +327,15 @@ bool AuthSession::validateSelf(const MTPUser &user) {
|
||||
|
||||
void AuthSession::saveSettingsDelayed(TimeMs delay) {
|
||||
Expects(this == &Auth());
|
||||
|
||||
_saveDataTimer.callOnce(delay);
|
||||
}
|
||||
|
||||
void AuthSession::checkAutoLock() {
|
||||
if (!Global::LocalPasscode() || App::passcoded()) return;
|
||||
if (!Global::LocalPasscode()
|
||||
|| Messenger::Instance().passcodeLocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Messenger::Instance().checkLocalTime();
|
||||
auto now = getms(true);
|
||||
@@ -327,7 +344,7 @@ void AuthSession::checkAutoLock() {
|
||||
auto notPlayingVideoForMs = now - settings().lastTimeVideoPlayedAt();
|
||||
auto checkTimeMs = qMin(idleForMs, notPlayingVideoForMs);
|
||||
if (checkTimeMs >= shouldLockInMs || (_shouldLockAt > 0 && now > _shouldLockAt + kAutoLockTimeoutLateMs)) {
|
||||
Messenger::Instance().setupPasscode();
|
||||
Messenger::Instance().lockByPasscode();
|
||||
} else {
|
||||
_shouldLockAt = now + (shouldLockInMs - checkTimeMs);
|
||||
_autoLockTimer.callOnce(shouldLockInMs - checkTimeMs);
|
||||
|
||||
@@ -13,6 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace base {
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
template <
|
||||
typename Key,
|
||||
typename Type,
|
||||
@@ -414,8 +417,9 @@ public:
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
const auto result = (range.second - range.first);
|
||||
impl().erase(range.first, range.second);
|
||||
return (range.second - range.first);
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator erase(const_iterator where) {
|
||||
@@ -424,6 +428,9 @@ public:
|
||||
iterator erase(const_iterator from, const_iterator till) {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
int erase(const Key &key) {
|
||||
return removeAll(key);
|
||||
}
|
||||
|
||||
iterator findFirst(const Key &key) {
|
||||
if (empty()
|
||||
|
||||
@@ -12,6 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace base {
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_set;
|
||||
|
||||
@@ -294,8 +297,9 @@ public:
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
const auto result = (range.second - range.first);
|
||||
impl().erase(range.first, range.second);
|
||||
return (range.second - range.first);
|
||||
return result;
|
||||
}
|
||||
|
||||
iterator erase(const_iterator where) {
|
||||
@@ -304,6 +308,9 @@ public:
|
||||
iterator erase(const_iterator from, const_iterator till) {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
int erase(const Type &value) {
|
||||
return removeAll(value);
|
||||
}
|
||||
|
||||
iterator findFirst(const Type &value) {
|
||||
if (empty()
|
||||
|
||||
@@ -1,487 +0,0 @@
|
||||
/*
|
||||
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 <memory>
|
||||
|
||||
#ifndef CUSTOM_LAMBDA_WRAP
|
||||
|
||||
#include <functional>
|
||||
#include "base/unique_function.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Function>
|
||||
using lambda = std::function<Function>;
|
||||
|
||||
template <typename Function>
|
||||
using lambda_once = unique_function<Function>;
|
||||
|
||||
namespace lambda_internal {
|
||||
|
||||
template <typename Lambda>
|
||||
struct lambda_call_type {
|
||||
using type = decltype(&Lambda::operator());
|
||||
};
|
||||
|
||||
} // namespace lambda_internal
|
||||
|
||||
template <typename Lambda>
|
||||
using lambda_call_type_t
|
||||
= typename lambda_internal::lambda_call_type<Lambda>::type;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#else // CUSTOM_LAMBDA_WRAP
|
||||
|
||||
#ifndef Assert
|
||||
#define LambdaAssertDefined
|
||||
#define Assert(v) ((v) ? ((void)0) : std::abort())
|
||||
#endif // Assert
|
||||
|
||||
#ifndef Unexpected
|
||||
#define LambdaUnexpectedDefined
|
||||
#define Unexpected(v) std::abort()
|
||||
#endif // Unexpected
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Function> class lambda_once;
|
||||
template <typename Function> class lambda;
|
||||
|
||||
// Get lambda type from a lambda template parameter.
|
||||
|
||||
namespace lambda_internal {
|
||||
|
||||
template <typename FunctionType>
|
||||
struct type_resolver;
|
||||
|
||||
template <typename Lambda, typename R, typename ...Args>
|
||||
struct type_resolver<R(Lambda::*)(Args...) const> {
|
||||
using type = lambda<R(Args...)>;
|
||||
static constexpr auto is_mutable = false;
|
||||
};
|
||||
|
||||
template <typename Lambda, typename R, typename ...Args>
|
||||
struct type_resolver<R(Lambda::*)(Args...)> {
|
||||
using type = lambda_once<R(Args...)>;
|
||||
static constexpr auto is_mutable = true;
|
||||
};
|
||||
|
||||
template <typename Lambda>
|
||||
struct type_helper {
|
||||
using type = typename type_resolver<decltype(&Lambda::operator())>::type;
|
||||
static constexpr auto is_mutable = type_resolver<decltype(&Lambda::operator())>::is_mutable;
|
||||
};
|
||||
|
||||
} // namespace lambda_internal
|
||||
|
||||
template <typename Lambda>
|
||||
using lambda_type = typename lambda_internal::type_helper<std::decay_t<Lambda>>::type;
|
||||
|
||||
namespace lambda_internal {
|
||||
|
||||
constexpr auto kFullStorageSize = 32U;
|
||||
static_assert(kFullStorageSize % sizeof(void*) == 0, "Invalid pointer size!");
|
||||
|
||||
constexpr auto kStorageSize = kFullStorageSize - sizeof(void*);
|
||||
using alignment = std::max_align_t;
|
||||
|
||||
template <typename Lambda>
|
||||
constexpr bool is_large = (sizeof(std::decay_t<Lambda>) > kStorageSize);
|
||||
|
||||
[[noreturn]] inline void bad_construct_copy(void *lambda, const void *source) {
|
||||
Unexpected("base::lambda bad_construct_copy() called!");
|
||||
}
|
||||
|
||||
template <typename Return, typename ...Args>
|
||||
[[noreturn]] Return bad_const_call(const void *lambda, Args...) {
|
||||
Unexpected("base::lambda bad_const_call() called!");
|
||||
}
|
||||
|
||||
template <typename Return, typename ...Args>
|
||||
struct vtable_base {
|
||||
using construct_copy_other_type = void(*)(void *, const void *); // dst, src
|
||||
using construct_move_other_type = void(*)(void *, void *); // dst, src
|
||||
using const_call_type = Return(*)(const void *, Args...);
|
||||
using call_type = Return(*)(void *, Args...);
|
||||
using destruct_type = void(*)(const void *);
|
||||
|
||||
vtable_base() = delete;
|
||||
vtable_base(const vtable_base &other) = delete;
|
||||
vtable_base &operator=(const vtable_base &other) = delete;
|
||||
|
||||
vtable_base(
|
||||
construct_copy_other_type construct_copy_other,
|
||||
construct_move_other_type construct_move_other,
|
||||
const_call_type const_call,
|
||||
call_type call,
|
||||
destruct_type destruct)
|
||||
: construct_copy_other(construct_copy_other)
|
||||
, construct_move_other(construct_move_other)
|
||||
, const_call(const_call)
|
||||
, call(call)
|
||||
, destruct(destruct) {
|
||||
}
|
||||
|
||||
const construct_copy_other_type construct_copy_other;
|
||||
const construct_move_other_type construct_move_other;
|
||||
const const_call_type const_call;
|
||||
const call_type call;
|
||||
const destruct_type destruct;
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda, bool IsLarge, typename Return, typename ...Args> struct vtable_once_impl;
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct vtable_once_impl<Lambda, true, Return, Args...> : public vtable_base<Return, Args...> {
|
||||
using JustLambda = std::decay_t<Lambda>;
|
||||
using LambdaPtr = std::unique_ptr<JustLambda>;
|
||||
using Parent = vtable_base<Return, Args...>;
|
||||
static void construct_move_other_method(void *storage, void *source) {
|
||||
auto source_lambda_ptr = static_cast<LambdaPtr*>(source);
|
||||
new (storage) LambdaPtr(std::move(*source_lambda_ptr));
|
||||
}
|
||||
static Return call_method(void *storage, Args... args) {
|
||||
return (**static_cast<LambdaPtr*>(storage))(std::forward<Args>(args)...);
|
||||
}
|
||||
static void destruct_method(const void *storage) {
|
||||
static_cast<const LambdaPtr*>(storage)->~LambdaPtr();
|
||||
}
|
||||
vtable_once_impl() : Parent(
|
||||
&bad_construct_copy,
|
||||
&vtable_once_impl::construct_move_other_method,
|
||||
&bad_const_call<Return, Args...>,
|
||||
&vtable_once_impl::call_method,
|
||||
&vtable_once_impl::destruct_method) {
|
||||
}
|
||||
|
||||
// Used directly.
|
||||
static void construct_move_lambda_method(void *storage, void *source) {
|
||||
auto source_lambda = static_cast<JustLambda*>(source);
|
||||
new (storage) LambdaPtr(std::make_unique<JustLambda>(static_cast<JustLambda&&>(*source_lambda)));
|
||||
}
|
||||
|
||||
protected:
|
||||
vtable_once_impl(
|
||||
typename Parent::construct_copy_other_type construct_copy_other,
|
||||
typename Parent::const_call_type const_call
|
||||
) : Parent(
|
||||
construct_copy_other,
|
||||
&vtable_once_impl::construct_move_other_method,
|
||||
const_call,
|
||||
&vtable_once_impl::call_method,
|
||||
&vtable_once_impl::destruct_method) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct vtable_once_impl<Lambda, false, Return, Args...> : public vtable_base<Return, Args...> {
|
||||
using JustLambda = std::decay_t<Lambda>;
|
||||
using Parent = vtable_base<Return, Args...>;
|
||||
static void construct_move_other_method(void *storage, void *source) {
|
||||
auto source_lambda = static_cast<JustLambda*>(source);
|
||||
new (storage) JustLambda(static_cast<JustLambda&&>(*source_lambda));
|
||||
}
|
||||
static Return call_method(void *storage, Args... args) {
|
||||
return (*static_cast<JustLambda*>(storage))(std::forward<Args>(args)...);
|
||||
}
|
||||
static void destruct_method(const void *storage) {
|
||||
static_cast<const JustLambda*>(storage)->~JustLambda();
|
||||
}
|
||||
vtable_once_impl() : Parent(
|
||||
&bad_construct_copy,
|
||||
&vtable_once_impl::construct_move_other_method,
|
||||
&bad_const_call<Return, Args...>,
|
||||
&vtable_once_impl::call_method,
|
||||
&vtable_once_impl::destruct_method) {
|
||||
}
|
||||
|
||||
// Used directly.
|
||||
static void construct_move_lambda_method(void *storage, void *source) {
|
||||
auto source_lambda = static_cast<JustLambda*>(source);
|
||||
new (storage) JustLambda(static_cast<JustLambda&&>(*source_lambda));
|
||||
}
|
||||
|
||||
protected:
|
||||
vtable_once_impl(
|
||||
typename Parent::construct_copy_other_type construct_copy_other,
|
||||
typename Parent::const_call_type const_call
|
||||
) : Parent(
|
||||
construct_copy_other,
|
||||
&vtable_once_impl::construct_move_other_method,
|
||||
const_call,
|
||||
&vtable_once_impl::call_method,
|
||||
&vtable_once_impl::destruct_method) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct vtable_once : public vtable_once_impl<Lambda, is_large<Lambda>, Return, Args...> {
|
||||
static const vtable_once instance;
|
||||
};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
const vtable_once<Lambda, Return, Args...> vtable_once<Lambda, Return, Args...>::instance = {};
|
||||
|
||||
template <typename Lambda, bool IsLarge, typename Return, typename ...Args> struct vtable_impl;
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct vtable_impl<Lambda, true, Return, Args...> : public vtable_once_impl<Lambda, true, Return, Args...> {
|
||||
using JustLambda = std::decay_t<Lambda>;
|
||||
using LambdaPtr = std::unique_ptr<JustLambda>;
|
||||
using Parent = vtable_once_impl<Lambda, true, Return, Args...>;
|
||||
static void construct_copy_other_method(void *storage, const void *source) {
|
||||
auto source_lambda = static_cast<const LambdaPtr*>(source);
|
||||
new (storage) LambdaPtr(std::make_unique<JustLambda>(*source_lambda->get()));
|
||||
}
|
||||
static Return const_call_method(const void *storage, Args... args) {
|
||||
auto lambda_ptr = static_cast<const LambdaPtr*>(storage)->get();
|
||||
return (*static_cast<const JustLambda*>(lambda_ptr))(std::forward<Args>(args)...);
|
||||
}
|
||||
vtable_impl() : Parent(
|
||||
&vtable_impl::construct_copy_other_method,
|
||||
&vtable_impl::const_call_method
|
||||
) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct vtable_impl<Lambda, false, Return, Args...> : public vtable_once_impl<Lambda, false, Return, Args...> {
|
||||
using JustLambda = std::decay_t<Lambda>;
|
||||
using Parent = vtable_once_impl<Lambda, false, Return, Args...>;
|
||||
static void construct_copy_other_method(void *storage, const void *source) {
|
||||
auto source_lambda = static_cast<const JustLambda*>(source);
|
||||
new (storage) JustLambda(static_cast<const JustLambda &>(*source_lambda));
|
||||
}
|
||||
static Return const_call_method(const void *storage, Args... args) {
|
||||
return (*static_cast<const JustLambda*>(storage))(std::forward<Args>(args)...);
|
||||
}
|
||||
vtable_impl() : Parent(
|
||||
&vtable_impl::construct_copy_other_method,
|
||||
&vtable_impl::const_call_method
|
||||
) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
struct vtable : public vtable_impl<Lambda, is_large<Lambda>, Return, Args...> {
|
||||
static const vtable instance;
|
||||
};
|
||||
|
||||
template <typename Lambda, typename Return, typename ...Args>
|
||||
const vtable<Lambda, Return, Args...> vtable<Lambda, Return, Args...>::instance = {};
|
||||
|
||||
} // namespace lambda_internal
|
||||
|
||||
template <typename Return, typename ...Args>
|
||||
class lambda_once<Return(Args...)> {
|
||||
using VTable = lambda_internal::vtable_base<Return, Args...>;
|
||||
|
||||
public:
|
||||
using return_type = Return;
|
||||
|
||||
lambda_once() {
|
||||
data_.vtable = nullptr;
|
||||
}
|
||||
lambda_once(const lambda_once &other) = delete;
|
||||
lambda_once &operator=(const lambda_once &other) = delete;
|
||||
|
||||
// Move construct / assign from the same type.
|
||||
lambda_once(lambda_once &&other) {
|
||||
if ((data_.vtable = other.data_.vtable)) {
|
||||
data_.vtable->construct_move_other(data_.storage, other.data_.storage);
|
||||
}
|
||||
}
|
||||
lambda_once &operator=(lambda_once &&other) {
|
||||
if (this != &other) {
|
||||
if (data_.vtable) {
|
||||
data_.vtable->destruct(data_.storage);
|
||||
}
|
||||
if ((data_.vtable = other.data_.vtable)) {
|
||||
data_.vtable->construct_move_other(data_.storage, other.data_.storage);
|
||||
data_.vtable->destruct(other.data_.storage);
|
||||
other.data_.vtable = nullptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move construct / assign from a derived type.
|
||||
lambda_once(lambda<Return(Args...)> &&other) {
|
||||
if ((data_.vtable = other.data_.vtable)) {
|
||||
data_.vtable->construct_move_other(data_.storage, other.data_.storage);
|
||||
data_.vtable->destruct(other.data_.storage);
|
||||
other.data_.vtable = nullptr;
|
||||
}
|
||||
}
|
||||
lambda_once &operator=(lambda<Return(Args...)> &&other) {
|
||||
if (this != &other) {
|
||||
if (data_.vtable) {
|
||||
data_.vtable->destruct(data_.storage);
|
||||
}
|
||||
if ((data_.vtable = other.data_.vtable)) {
|
||||
data_.vtable->construct_move_other(data_.storage, other.data_.storage);
|
||||
data_.vtable->destruct(other.data_.storage);
|
||||
other.data_.vtable = nullptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy construct / assign from a derived type.
|
||||
lambda_once(const lambda<Return(Args...)> &other) {
|
||||
if ((data_.vtable = other.data_.vtable)) {
|
||||
data_.vtable->construct_copy_other(data_.storage, other.data_.storage);
|
||||
}
|
||||
}
|
||||
lambda_once &operator=(const lambda<Return(Args...)> &other) {
|
||||
if (this != &other) {
|
||||
if (data_.vtable) {
|
||||
data_.vtable->destruct(data_.storage);
|
||||
}
|
||||
if ((data_.vtable = other.data_.vtable)) {
|
||||
data_.vtable->construct_copy_other(data_.storage, other.data_.storage);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy / move construct / assign from an arbitrary type.
|
||||
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
|
||||
lambda_once(Lambda other) {
|
||||
data_.vtable = &lambda_internal::vtable_once<Lambda, Return, Args...>::instance;
|
||||
lambda_internal::vtable_once<Lambda, Return, Args...>::construct_move_lambda_method(data_.storage, &other);
|
||||
}
|
||||
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
|
||||
lambda_once &operator=(Lambda other) {
|
||||
if (data_.vtable) {
|
||||
data_.vtable->destruct(data_.storage);
|
||||
}
|
||||
data_.vtable = &lambda_internal::vtable_once<Lambda, Return, Args...>::instance;
|
||||
lambda_internal::vtable_once<Lambda, Return, Args...>::construct_move_lambda_method(data_.storage, &other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(lambda_once &other) {
|
||||
if (this != &other) {
|
||||
std::swap(*this, other);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename = std::enable_if_t<(sizeof...(Args) == sizeof...(OtherArgs))>>
|
||||
inline Return operator()(OtherArgs&&... args) {
|
||||
Assert(data_.vtable != nullptr);
|
||||
return data_.vtable->call(
|
||||
data_.storage,
|
||||
std::forward<OtherArgs>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return (data_.vtable != nullptr);
|
||||
}
|
||||
|
||||
~lambda_once() {
|
||||
if (data_.vtable) {
|
||||
data_.vtable->destruct(data_.storage);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
struct Private {
|
||||
};
|
||||
lambda_once(const VTable *vtable, const Private &) {
|
||||
data_.vtable = vtable;
|
||||
}
|
||||
|
||||
struct Data {
|
||||
char storage[lambda_internal::kStorageSize];
|
||||
const VTable *vtable;
|
||||
};
|
||||
union {
|
||||
lambda_internal::alignment alignment_;
|
||||
char raw_[lambda_internal::kFullStorageSize];
|
||||
Data data_;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template <typename Return, typename ...Args>
|
||||
class lambda<Return(Args...)> final : public lambda_once<Return(Args...)> {
|
||||
using Parent = lambda_once<Return(Args...)>;
|
||||
|
||||
public:
|
||||
lambda() = default;
|
||||
|
||||
// Move construct / assign from the same type.
|
||||
lambda(lambda<Return(Args...)> &&other) : Parent(std::move(other)) {
|
||||
}
|
||||
lambda &operator=(lambda<Return(Args...)> &&other) {
|
||||
Parent::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy construct / assign from the same type.
|
||||
lambda(const lambda<Return(Args...)> &other) : Parent(other) {
|
||||
}
|
||||
lambda &operator=(const lambda<Return(Args...)> &other) {
|
||||
Parent::operator=(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Copy / move construct / assign from an arbitrary type.
|
||||
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
|
||||
lambda(Lambda other) : Parent(&lambda_internal::vtable<Lambda, Return, Args...>::instance, typename Parent::Private()) {
|
||||
lambda_internal::vtable<Lambda, Return, Args...>::construct_move_lambda_method(this->data_.storage, &other);
|
||||
}
|
||||
template <typename Lambda, typename = std::enable_if_t<std::is_convertible<decltype(std::declval<Lambda>()(std::declval<Args>()...)),Return>::value>>
|
||||
lambda &operator=(Lambda other) {
|
||||
if (this->data_.vtable) {
|
||||
this->data_.vtable->destruct(this->data_.storage);
|
||||
}
|
||||
this->data_.vtable = &lambda_internal::vtable<Lambda, Return, Args...>::instance;
|
||||
lambda_internal::vtable<Lambda, Return, Args...>::construct_move_lambda_method(this->data_.storage, &other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename = std::enable_if_t<(sizeof...(Args) == sizeof...(OtherArgs))>>
|
||||
inline Return operator()(OtherArgs&&... args) const {
|
||||
Assert(this->data_.vtable != nullptr);
|
||||
return this->data_.vtable->const_call(
|
||||
this->data_.storage,
|
||||
std::forward<OtherArgs>(args)...);
|
||||
}
|
||||
|
||||
void swap(lambda &other) {
|
||||
if (this != &other) {
|
||||
std::swap(*this, other);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#ifdef LambdaAssertDefined
|
||||
#undef Assert
|
||||
#endif // LambdaAssertDefined
|
||||
|
||||
#ifdef LambdaUnexpectedDefined
|
||||
#undef Unexpected
|
||||
#endif // LambdaUnexpectedDefined
|
||||
|
||||
#endif // CUSTOM_LAMBDA_WRAP
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
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 <QPointer>
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
// Guard lambda call by QObject* or has_weak_ptr* pointers.
|
||||
|
||||
namespace base {
|
||||
namespace lambda_internal {
|
||||
|
||||
template <typename Lambda>
|
||||
class guard_with_QObject {
|
||||
public:
|
||||
template <typename OtherLambda>
|
||||
guard_with_QObject(const QObject *object, OtherLambda &&other)
|
||||
: _guard(object)
|
||||
, _callable(std::forward<OtherLambda>(other)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
|
||||
Return operator()(OtherArgs &&...args) {
|
||||
return _guard
|
||||
? _callable(std::forward<OtherArgs>(args)...)
|
||||
: Return();
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
|
||||
Return operator()(OtherArgs &&...args) const {
|
||||
return _guard
|
||||
? _callable(std::forward<OtherArgs>(args)...)
|
||||
: Return();
|
||||
}
|
||||
|
||||
private:
|
||||
QPointer<const QObject> _guard;
|
||||
Lambda _callable;
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda>
|
||||
class guard_with_weak {
|
||||
public:
|
||||
template <typename OtherLambda>
|
||||
guard_with_weak(
|
||||
const base::has_weak_ptr *object,
|
||||
OtherLambda &&other)
|
||||
: _guard(base::make_weak(object))
|
||||
, _callable(std::forward<OtherLambda>(other)) {
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
|
||||
Return operator()(OtherArgs &&...args) {
|
||||
return _guard
|
||||
? _callable(std::forward<OtherArgs>(args)...)
|
||||
: Return();
|
||||
}
|
||||
|
||||
template <
|
||||
typename ...OtherArgs,
|
||||
typename Return = decltype(std::declval<Lambda>()(std::declval<OtherArgs>()...))>
|
||||
Return operator()(OtherArgs &&...args) const {
|
||||
return _guard
|
||||
? _callable(std::forward<OtherArgs>(args)...)
|
||||
: Return();
|
||||
}
|
||||
|
||||
private:
|
||||
base::weak_ptr<const base::has_weak_ptr> _guard;
|
||||
Lambda _callable;
|
||||
|
||||
};
|
||||
|
||||
template <typename Lambda>
|
||||
struct lambda_call_type<guard_with_QObject<Lambda>> {
|
||||
using type = lambda_call_type_t<Lambda>;
|
||||
};
|
||||
|
||||
template <typename Lambda>
|
||||
struct lambda_call_type<guard_with_weak<Lambda>> {
|
||||
using type = lambda_call_type_t<Lambda>;
|
||||
};
|
||||
|
||||
} // namespace lambda_internal
|
||||
|
||||
template <typename Lambda>
|
||||
inline auto lambda_guarded(const QObject *object, Lambda &&lambda) {
|
||||
using Guarded = lambda_internal::guard_with_QObject<
|
||||
std::decay_t<Lambda>>;
|
||||
return Guarded(object, std::forward<Lambda>(lambda));
|
||||
}
|
||||
|
||||
template <typename Lambda>
|
||||
inline auto lambda_guarded(
|
||||
const base::has_weak_ptr *object,
|
||||
Lambda &&lambda) {
|
||||
using Guarded = lambda_internal::guard_with_weak<
|
||||
std::decay_t<Lambda>>;
|
||||
return Guarded(object, std::forward<Lambda>(lambda));
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
@@ -15,19 +15,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
using ObservableCallHandlers = base::lambda<void()>;
|
||||
using ObservableCallHandlers = Fn<void()>;
|
||||
void RegisterPendingObservable(ObservableCallHandlers *handlers);
|
||||
void UnregisterActiveObservable(ObservableCallHandlers *handlers);
|
||||
void UnregisterObservable(ObservableCallHandlers *handlers);
|
||||
|
||||
template <typename EventType>
|
||||
struct SubscriptionHandlerHelper {
|
||||
using type = base::lambda<void(parameter_type<EventType>)>;
|
||||
using type = Fn<void(parameter_type<EventType>)>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SubscriptionHandlerHelper<void> {
|
||||
using type = base::lambda<void()>;
|
||||
using type = Fn<void()>;
|
||||
};
|
||||
|
||||
template <typename EventType>
|
||||
|
||||
@@ -7,10 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/bytes.h"
|
||||
|
||||
extern "C" {
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "base/bytes.h"
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/crypto.h>
|
||||
} // extern "C"
|
||||
|
||||
namespace openssl {
|
||||
|
||||
@@ -61,7 +66,7 @@ public:
|
||||
explicit BigNum(unsigned int word) : BigNum() {
|
||||
setWord(word);
|
||||
}
|
||||
explicit BigNum(base::const_byte_span bytes) : BigNum() {
|
||||
explicit BigNum(bytes::const_span bytes) : BigNum() {
|
||||
setBytes(bytes);
|
||||
}
|
||||
|
||||
@@ -70,7 +75,7 @@ public:
|
||||
_failed = true;
|
||||
}
|
||||
}
|
||||
void setBytes(base::const_byte_span bytes) {
|
||||
void setBytes(bytes::const_span bytes) {
|
||||
if (!BN_bin2bn(
|
||||
reinterpret_cast<const unsigned char*>(bytes.data()),
|
||||
bytes.size(),
|
||||
@@ -162,12 +167,12 @@ public:
|
||||
return failed() ? 0 : BN_num_bytes(raw());
|
||||
}
|
||||
|
||||
base::byte_vector getBytes() const {
|
||||
bytes::vector getBytes() const {
|
||||
if (failed()) {
|
||||
return base::byte_vector();
|
||||
return {};
|
||||
}
|
||||
auto length = BN_num_bytes(raw());
|
||||
auto result = base::byte_vector(length, gsl::byte());
|
||||
auto result = bytes::vector(length);
|
||||
auto resultSize = BN_bn2bin(
|
||||
raw(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
@@ -207,20 +212,35 @@ inline BigNum operator-(const BigNum &a, const BigNum &b) {
|
||||
return result;
|
||||
}
|
||||
|
||||
inline base::byte_array<SHA256_DIGEST_LENGTH> Sha256(base::const_byte_span bytes) {
|
||||
auto result = base::byte_array<SHA256_DIGEST_LENGTH>();
|
||||
SHA256(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size(), reinterpret_cast<unsigned char*>(result.data()));
|
||||
inline bytes::vector Sha512(bytes::const_span data) {
|
||||
auto result = bytes::vector(SHA512_DIGEST_LENGTH);
|
||||
SHA512(
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline base::byte_array<SHA_DIGEST_LENGTH> Sha1(base::const_byte_span bytes) {
|
||||
auto result = base::byte_array<SHA_DIGEST_LENGTH>();
|
||||
SHA1(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size(), reinterpret_cast<unsigned char*>(result.data()));
|
||||
inline bytes::vector Sha256(bytes::const_span data) {
|
||||
auto result = bytes::vector(SHA256_DIGEST_LENGTH);
|
||||
SHA256(
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int FillRandom(base::byte_span bytes) {
|
||||
return RAND_bytes(reinterpret_cast<unsigned char*>(bytes.data()), bytes.size());
|
||||
inline bytes::vector Sha1(bytes::const_span data) {
|
||||
auto result = bytes::vector(SHA_DIGEST_LENGTH);
|
||||
SHA1(
|
||||
reinterpret_cast<const unsigned char*>(data.data()),
|
||||
data.size(),
|
||||
reinterpret_cast<unsigned char*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void AddRandomSeed(bytes::const_span data) {
|
||||
RAND_seed(data.data(), data.size());
|
||||
}
|
||||
|
||||
} // namespace openssl
|
||||
|
||||
@@ -87,6 +87,15 @@ public:
|
||||
return _impl >= other._impl;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T &set(Args &&...args) {
|
||||
_impl.template set<T>(std::forward<Args>(args)...);
|
||||
return get_unchecked<T>();
|
||||
}
|
||||
void clear() {
|
||||
_impl.template set<none_type>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) is() const {
|
||||
return _impl.template is<T>();
|
||||
@@ -146,28 +155,34 @@ using optional_chain_result_t = typename optional_chain_result<Type>::type;
|
||||
|
||||
template <typename Type>
|
||||
class optional : public optional_variant<Type> {
|
||||
using parent = optional_variant<Type>;
|
||||
|
||||
public:
|
||||
using optional_variant<Type>::optional_variant;
|
||||
using parent::parent;
|
||||
|
||||
Type &operator*() {
|
||||
auto result = get_if<Type>(this);
|
||||
Expects(result != nullptr);
|
||||
return *result;
|
||||
Expects(parent::template is<Type>());
|
||||
|
||||
return parent::template get_unchecked<Type>();
|
||||
}
|
||||
const Type &operator*() const {
|
||||
auto result = get_if<Type>(this);
|
||||
Expects(result != nullptr);
|
||||
return *result;
|
||||
Expects(parent::template is<Type>());
|
||||
|
||||
return parent::template get_unchecked<Type>();
|
||||
}
|
||||
Type *operator->() {
|
||||
auto result = get_if<Type>(this);
|
||||
Expects(result != nullptr);
|
||||
return result;
|
||||
Expects(parent::template is<Type>());
|
||||
|
||||
return std::addressof(parent::template get_unchecked<Type>());
|
||||
}
|
||||
const Type *operator->() const {
|
||||
auto result = get_if<Type>(this);
|
||||
Expects(result != nullptr);
|
||||
return result;
|
||||
Expects(parent::template is<Type>());
|
||||
|
||||
return std::addressof(parent::template get_unchecked<Type>());
|
||||
}
|
||||
template <typename ...Args>
|
||||
Type &emplace(Args &&...args) {
|
||||
return parent::template set<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -41,7 +41,18 @@ QMap<QString, QString> url_parse_params(
|
||||
bool is_ipv6(const QString &ip) {
|
||||
//static const auto regexp = QRegularExpression("^[a-fA-F0-9:]+$");
|
||||
//return regexp.match(ip).hasMatch();
|
||||
return ip.indexOf(':') >= 0;
|
||||
return ip.indexOf('.') < 0 && ip.indexOf(':') >= 0;
|
||||
}
|
||||
|
||||
QString url_append_query_or_hash(const QString &url, const QString &add) {
|
||||
const auto query = url.lastIndexOf('?');
|
||||
if (query < 0) {
|
||||
return url + '?' + add;
|
||||
}
|
||||
const auto hash = url.lastIndexOf('#');
|
||||
return url
|
||||
+ (query >= 0 && query > url.lastIndexOf('#') ? '&' : '?')
|
||||
+ add;
|
||||
}
|
||||
|
||||
} // namespace qthelp
|
||||
|
||||
@@ -22,7 +22,11 @@ enum class UrlParamNameTransform {
|
||||
ToLower,
|
||||
};
|
||||
// Parses a string like "p1=v1&p2=v2&..&pn=vn" to a map.
|
||||
QMap<QString, QString> url_parse_params(const QString ¶ms, UrlParamNameTransform transform = UrlParamNameTransform::NoTransform);
|
||||
QMap<QString, QString> url_parse_params(
|
||||
const QString ¶ms,
|
||||
UrlParamNameTransform transform = UrlParamNameTransform::NoTransform);
|
||||
|
||||
QString url_append_query_or_hash(const QString &url, const QString &add);
|
||||
|
||||
bool is_ipv6(const QString &ip);
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ QObject *TimersAdjuster() {
|
||||
|
||||
Timer::Timer(
|
||||
not_null<QThread*> thread,
|
||||
base::lambda<void()> callback)
|
||||
Fn<void()> callback)
|
||||
: Timer(std::move(callback)) {
|
||||
moveToThread(thread);
|
||||
}
|
||||
|
||||
|
||||
Timer::Timer(base::lambda<void()> callback)
|
||||
Timer::Timer(Fn<void()> callback)
|
||||
: QObject(nullptr)
|
||||
, _callback(std::move(callback))
|
||||
, _type(Qt::PreciseTimer)
|
||||
@@ -114,7 +114,7 @@ void Timer::timerEvent(QTimerEvent *e) {
|
||||
|
||||
int DelayedCallTimer::call(
|
||||
TimeMs timeout,
|
||||
lambda_once<void()> callback,
|
||||
FnMut<void()> callback,
|
||||
Qt::TimerType type) {
|
||||
Expects(timeout >= 0);
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/lambda.h"
|
||||
#include "base/observer.h"
|
||||
|
||||
namespace base {
|
||||
@@ -16,15 +15,15 @@ class Timer final : private QObject {
|
||||
public:
|
||||
explicit Timer(
|
||||
not_null<QThread*> thread,
|
||||
base::lambda<void()> callback = nullptr);
|
||||
explicit Timer(base::lambda<void()> callback = nullptr);
|
||||
Fn<void()> callback = nullptr);
|
||||
explicit Timer(Fn<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(TimeMs timeout) {
|
||||
constexpr auto kThreshold = TimeMs(1000);
|
||||
return (timeout > kThreshold) ? Qt::CoarseTimer : Qt::PreciseTimer;
|
||||
}
|
||||
|
||||
void setCallback(base::lambda<void()> callback) {
|
||||
void setCallback(Fn<void()> callback) {
|
||||
_callback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -74,7 +73,7 @@ private:
|
||||
return static_cast<Repeat>(_repeat);
|
||||
}
|
||||
|
||||
base::lambda<void()> _callback;
|
||||
Fn<void()> _callback;
|
||||
TimeMs _next = 0;
|
||||
int _timeout = 0;
|
||||
int _timerId = 0;
|
||||
@@ -87,7 +86,7 @@ private:
|
||||
|
||||
class DelayedCallTimer final : private QObject {
|
||||
public:
|
||||
int call(TimeMs timeout, lambda_once<void()> callback) {
|
||||
int call(TimeMs timeout, FnMut<void()> callback) {
|
||||
return call(
|
||||
timeout,
|
||||
std::move(callback),
|
||||
@@ -96,7 +95,7 @@ public:
|
||||
|
||||
int call(
|
||||
TimeMs timeout,
|
||||
lambda_once<void()> callback,
|
||||
FnMut<void()> callback,
|
||||
Qt::TimerType type);
|
||||
void cancel(int callId);
|
||||
|
||||
@@ -104,7 +103,7 @@ protected:
|
||||
void timerEvent(QTimerEvent *e) override;
|
||||
|
||||
private:
|
||||
base::flat_map<int, lambda_once<void()>> _callbacks;
|
||||
base::flat_map<int, FnMut<void()>> _callbacks;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#ifndef Unexpected
|
||||
#define Unexpected(message) std::abort()
|
||||
#define UniqueFunctionUnexpected
|
||||
@@ -47,7 +49,7 @@ public:
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
decltype(auto) operator()(Args &&...args) const {
|
||||
decltype(auto) operator()(Args &&...args) {
|
||||
return _value(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -59,7 +61,7 @@ private:
|
||||
Unexpected("Attempt to copy-assign a move-only type.");
|
||||
}
|
||||
|
||||
mutable Callable _value;
|
||||
Callable _value;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwindow.h"
|
||||
|
||||
QPointer<Ui::RoundButton> BoxContent::addButton(
|
||||
base::lambda<QString()> textFactory,
|
||||
base::lambda<void()> clickCallback) {
|
||||
Fn<QString()> textFactory,
|
||||
Fn<void()> clickCallback) {
|
||||
return addButton(
|
||||
std::move(textFactory),
|
||||
std::move(clickCallback),
|
||||
@@ -29,11 +29,11 @@ QPointer<Ui::RoundButton> BoxContent::addButton(
|
||||
}
|
||||
|
||||
QPointer<Ui::RoundButton> BoxContent::addLeftButton(
|
||||
base::lambda<QString()> textFactory,
|
||||
base::lambda<void()> clickCallback) {
|
||||
Fn<QString()> textFactory,
|
||||
Fn<void()> clickCallback) {
|
||||
return getDelegate()->addLeftButton(
|
||||
std::move(textFactory),
|
||||
std::move(clickCallback),
|
||||
std::move(textFactory),
|
||||
std::move(clickCallback),
|
||||
st::defaultBoxButton);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ void BoxContent::finishPrepare() {
|
||||
|
||||
void BoxContent::finishScrollCreate() {
|
||||
Expects(_scroll != nullptr);
|
||||
|
||||
_scroll->show();
|
||||
updateScrollAreaGeometry();
|
||||
connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll()));
|
||||
connect(_scroll, SIGNAL(innerResized()), this, SLOT(onInnerResize()));
|
||||
@@ -188,6 +190,14 @@ void BoxContent::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void BoxContent::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Escape && !_closeByEscape) {
|
||||
e->accept();
|
||||
} else {
|
||||
RpWidget::keyPressEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
void BoxContent::updateScrollAreaGeometry() {
|
||||
auto newScrollHeight = height() - _innerTopSkip - _innerBottomSkip;
|
||||
auto changed = (_scroll->height() != newScrollHeight);
|
||||
@@ -225,8 +235,9 @@ void BoxContent::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
AbstractBox::AbstractBox(QWidget *parent, Window::Controller *controller, object_ptr<BoxContent> content) : LayerWidget(parent)
|
||||
, _controller(controller)
|
||||
AbstractBox::AbstractBox(not_null<Window::LayerStackWidget*> layer, object_ptr<BoxContent> content)
|
||||
: LayerWidget(layer)
|
||||
, _layer(layer)
|
||||
, _content(std::move(content)) {
|
||||
subscribe(Lang::Current().updated(), [this] { refreshLang(); });
|
||||
_content->setParent(this);
|
||||
@@ -287,7 +298,7 @@ void AbstractBox::parentResized() {
|
||||
update();
|
||||
}
|
||||
|
||||
void AbstractBox::setTitle(base::lambda<TextWithEntities()> titleFactory) {
|
||||
void AbstractBox::setTitle(Fn<TextWithEntities()> titleFactory) {
|
||||
_titleFactory = std::move(titleFactory);
|
||||
refreshTitle();
|
||||
}
|
||||
@@ -308,11 +319,19 @@ void AbstractBox::refreshTitle() {
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractBox::setAdditionalTitle(base::lambda<QString()> additionalFactory) {
|
||||
void AbstractBox::setAdditionalTitle(Fn<QString()> additionalFactory) {
|
||||
_additionalTitleFactory = std::move(additionalFactory);
|
||||
refreshAdditionalTitle();
|
||||
}
|
||||
|
||||
void AbstractBox::setCloseByOutsideClick(bool close) {
|
||||
_closeByOutsideClick = close;
|
||||
}
|
||||
|
||||
bool AbstractBox::closeByOutsideClick() const {
|
||||
return _closeByOutsideClick;
|
||||
}
|
||||
|
||||
void AbstractBox::refreshAdditionalTitle() {
|
||||
_additionalTitle = _additionalTitleFactory ? _additionalTitleFactory() : QString();
|
||||
update();
|
||||
@@ -328,6 +347,13 @@ bool AbstractBox::hasTitle() const {
|
||||
return (_title != nullptr) || !_additionalTitle.isEmpty();
|
||||
}
|
||||
|
||||
void AbstractBox::showBox(
|
||||
object_ptr<BoxContent> box,
|
||||
LayerOptions options,
|
||||
anim::type animated) {
|
||||
_layer->showBox(std::move(box), options, animated);
|
||||
}
|
||||
|
||||
void AbstractBox::updateSize() {
|
||||
setDimensions(width(), _maxContentHeight);
|
||||
}
|
||||
@@ -363,7 +389,7 @@ void AbstractBox::clearButtons() {
|
||||
_leftButton.destroy();
|
||||
}
|
||||
|
||||
QPointer<Ui::RoundButton> AbstractBox::addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) {
|
||||
QPointer<Ui::RoundButton> AbstractBox::addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) {
|
||||
_buttons.push_back(object_ptr<Ui::RoundButton>(this, std::move(textFactory), st));
|
||||
auto result = QPointer<Ui::RoundButton>(_buttons.back());
|
||||
result->setClickedCallback(std::move(clickCallback));
|
||||
@@ -372,7 +398,7 @@ QPointer<Ui::RoundButton> AbstractBox::addButton(base::lambda<QString()> textFac
|
||||
return result;
|
||||
}
|
||||
|
||||
QPointer<Ui::RoundButton> AbstractBox::addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) {
|
||||
QPointer<Ui::RoundButton> AbstractBox::addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) {
|
||||
_leftButton = object_ptr<Ui::RoundButton>(this, std::move(textFactory), st);
|
||||
auto result = QPointer<Ui::RoundButton>(_leftButton);
|
||||
result->setClickedCallback(std::move(clickCallback));
|
||||
@@ -436,11 +462,13 @@ void AbstractBox::keyPressEvent(QKeyEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
BoxContentDivider::BoxContentDivider(QWidget *parent) : RpWidget(parent) {
|
||||
BoxContentDivider::BoxContentDivider(QWidget *parent)
|
||||
: BoxContentDivider(parent, st::rightsDividerHeight) {
|
||||
}
|
||||
|
||||
int BoxContentDivider::resizeGetHeight(int newWidth) {
|
||||
return st::rightsDividerHeight;
|
||||
BoxContentDivider::BoxContentDivider(QWidget *parent, int height)
|
||||
: RpWidget(parent) {
|
||||
resize(width(), height);
|
||||
}
|
||||
|
||||
void BoxContentDivider::paintEvent(QPaintEvent *e) {
|
||||
|
||||
@@ -23,28 +23,39 @@ class FlatLabel;
|
||||
class FadeShadow;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
class BoxContent;
|
||||
|
||||
class BoxContentDelegate {
|
||||
public:
|
||||
virtual Window::Controller *controller() const = 0;
|
||||
|
||||
virtual void setLayerType(bool layerType) = 0;
|
||||
virtual void setTitle(base::lambda<TextWithEntities()> titleFactory) = 0;
|
||||
virtual void setAdditionalTitle(base::lambda<QString()> additionalFactory) = 0;
|
||||
virtual void setTitle(Fn<TextWithEntities()> titleFactory) = 0;
|
||||
virtual void setAdditionalTitle(Fn<QString()> additionalFactory) = 0;
|
||||
virtual void setCloseByOutsideClick(bool close) = 0;
|
||||
|
||||
virtual void clearButtons() = 0;
|
||||
virtual QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
|
||||
virtual QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) = 0;
|
||||
virtual QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) = 0;
|
||||
virtual QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) = 0;
|
||||
virtual void updateButtonsPositions() = 0;
|
||||
|
||||
virtual void showBox(
|
||||
object_ptr<BoxContent> box,
|
||||
LayerOptions options,
|
||||
anim::type animated) = 0;
|
||||
virtual void setDimensions(int newWidth, int maxHeight) = 0;
|
||||
virtual void setNoContentMargin(bool noContentMargin) = 0;
|
||||
virtual bool isBoxShown() const = 0;
|
||||
virtual void closeBox() = 0;
|
||||
|
||||
template <typename BoxType>
|
||||
QPointer<BoxType> show(
|
||||
object_ptr<BoxType> content,
|
||||
LayerOptions options = LayerOption::KeepOther,
|
||||
anim::type animated = anim::type::normal) {
|
||||
auto result = QPointer<BoxType>(content.data());
|
||||
showBox(std::move(content), options, animated);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class BoxContent : public Ui::RpWidget, protected base::Subscriber {
|
||||
@@ -62,28 +73,34 @@ public:
|
||||
getDelegate()->closeBox();
|
||||
}
|
||||
|
||||
void setTitle(base::lambda<QString()> titleFactory) {
|
||||
void setTitle(Fn<QString()> titleFactory) {
|
||||
if (titleFactory) {
|
||||
getDelegate()->setTitle([titleFactory] { return TextWithEntities { titleFactory(), EntitiesInText() }; });
|
||||
} else {
|
||||
getDelegate()->setTitle(base::lambda<TextWithEntities()>());
|
||||
getDelegate()->setTitle(Fn<TextWithEntities()>());
|
||||
}
|
||||
}
|
||||
void setTitle(base::lambda<TextWithEntities()> titleFactory) {
|
||||
void setTitle(Fn<TextWithEntities()> titleFactory) {
|
||||
getDelegate()->setTitle(std::move(titleFactory));
|
||||
}
|
||||
void setAdditionalTitle(base::lambda<QString()> additional) {
|
||||
void setAdditionalTitle(Fn<QString()> additional) {
|
||||
getDelegate()->setAdditionalTitle(std::move(additional));
|
||||
}
|
||||
void setCloseByEscape(bool close) {
|
||||
_closeByEscape = close;
|
||||
}
|
||||
void setCloseByOutsideClick(bool close) {
|
||||
getDelegate()->setCloseByOutsideClick(close);
|
||||
}
|
||||
|
||||
void scrollToWidget(not_null<QWidget*> widget);
|
||||
|
||||
void clearButtons() {
|
||||
getDelegate()->clearButtons();
|
||||
}
|
||||
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback);
|
||||
QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback);
|
||||
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) {
|
||||
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback);
|
||||
QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback);
|
||||
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) {
|
||||
return getDelegate()->addButton(std::move(textFactory), std::move(clickCallback), st);
|
||||
}
|
||||
void updateButtonsGeometry() {
|
||||
@@ -94,7 +111,12 @@ public:
|
||||
setFocus();
|
||||
}
|
||||
|
||||
base::Observable<void> boxClosing;
|
||||
rpl::producer<> boxClosing() const {
|
||||
return _boxClosingStream.events();
|
||||
}
|
||||
void notifyBoxClosing() {
|
||||
_boxClosingStream.fire({});
|
||||
}
|
||||
|
||||
void setDelegate(BoxContentDelegate *newDelegate) {
|
||||
_delegate = newDelegate;
|
||||
@@ -103,10 +125,6 @@ public:
|
||||
finishPrepare();
|
||||
}
|
||||
|
||||
Window::Controller *controller() {
|
||||
return getDelegate()->controller();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void onScrollToY(int top, int bottom = -1);
|
||||
|
||||
@@ -167,6 +185,11 @@ protected:
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
|
||||
not_null<BoxContentDelegate*> getDelegate() const {
|
||||
return _delegate;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void onScroll();
|
||||
@@ -184,14 +207,11 @@ private:
|
||||
void updateShadowsVisibility();
|
||||
object_ptr<TWidget> doTakeInnerWidget();
|
||||
|
||||
BoxContentDelegate *getDelegate() const {
|
||||
Expects(_delegate != nullptr);
|
||||
return _delegate;
|
||||
}
|
||||
BoxContentDelegate *_delegate = nullptr;
|
||||
|
||||
bool _preparing = false;
|
||||
bool _noContentMargin = false;
|
||||
bool _closeByEscape = true;
|
||||
int _innerTopSkip = 0;
|
||||
int _innerBottomSkip = 0;
|
||||
object_ptr<Ui::ScrollArea> _scroll = { nullptr };
|
||||
@@ -201,6 +221,8 @@ private:
|
||||
object_ptr<QTimer> _draggingScrollTimer = { nullptr };
|
||||
int _draggingScrollDelta = 0;
|
||||
|
||||
rpl::event_stream<> _boxClosingStream;
|
||||
|
||||
};
|
||||
|
||||
class AbstractBox
|
||||
@@ -208,20 +230,23 @@ class AbstractBox
|
||||
, public BoxContentDelegate
|
||||
, protected base::Subscriber {
|
||||
public:
|
||||
AbstractBox(QWidget *parent, Window::Controller *controller, object_ptr<BoxContent> content);
|
||||
AbstractBox(
|
||||
not_null<Window::LayerStackWidget*> layer,
|
||||
object_ptr<BoxContent> content);
|
||||
|
||||
Window::Controller *controller() const override {
|
||||
return _controller;
|
||||
}
|
||||
void parentResized() override;
|
||||
|
||||
void setLayerType(bool layerType) override;
|
||||
void setTitle(base::lambda<TextWithEntities()> titleFactory) override;
|
||||
void setAdditionalTitle(base::lambda<QString()> additionalFactory) override;
|
||||
void setTitle(Fn<TextWithEntities()> titleFactory) override;
|
||||
void setAdditionalTitle(Fn<QString()> additionalFactory) override;
|
||||
void showBox(
|
||||
object_ptr<BoxContent> box,
|
||||
LayerOptions options,
|
||||
anim::type animated) override;
|
||||
|
||||
void clearButtons() override;
|
||||
QPointer<Ui::RoundButton> addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
|
||||
QPointer<Ui::RoundButton> addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback, const style::RoundButton &st) override;
|
||||
QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) override;
|
||||
QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) override;
|
||||
void updateButtonsPositions() override;
|
||||
|
||||
void setDimensions(int newWidth, int maxHeight) override;
|
||||
@@ -240,6 +265,9 @@ public:
|
||||
closeLayer();
|
||||
}
|
||||
|
||||
void setCloseByOutsideClick(bool close) override;
|
||||
bool closeByOutsideClick() const override;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
@@ -249,7 +277,7 @@ protected:
|
||||
_content->setInnerFocus();
|
||||
}
|
||||
void closeHook() override {
|
||||
_content->boxClosing.notify(true);
|
||||
_content->notifyBoxClosing();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -268,7 +296,7 @@ private:
|
||||
int countRealHeight() const;
|
||||
void updateSize();
|
||||
|
||||
Window::Controller *_controller = nullptr;
|
||||
not_null<Window::LayerStackWidget*> _layer;
|
||||
int _fullHeight = 0;
|
||||
|
||||
bool _noContentMargin = false;
|
||||
@@ -276,12 +304,13 @@ private:
|
||||
object_ptr<BoxContent> _content;
|
||||
|
||||
object_ptr<Ui::FlatLabel> _title = { nullptr };
|
||||
base::lambda<TextWithEntities()> _titleFactory;
|
||||
Fn<TextWithEntities()> _titleFactory;
|
||||
QString _additionalTitle;
|
||||
base::lambda<QString()> _additionalTitleFactory;
|
||||
Fn<QString()> _additionalTitleFactory;
|
||||
int _titleLeft = 0;
|
||||
int _titleTop = 0;
|
||||
bool _layerType = false;
|
||||
bool _closeByOutsideClick = true;
|
||||
|
||||
std::vector<object_ptr<Ui::RoundButton>> _buttons;
|
||||
object_ptr<Ui::RoundButton> _leftButton = { nullptr };
|
||||
@@ -291,9 +320,9 @@ private:
|
||||
class BoxContentDivider : public Ui::RpWidget {
|
||||
public:
|
||||
BoxContentDivider(QWidget *parent);
|
||||
BoxContentDivider(QWidget *parent, int height);
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxGroupChannelTitle = 255;
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxBioLength = 70;
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
@@ -57,7 +57,7 @@ QString PeerFloodErrorText(PeerFloodType type) {
|
||||
|
||||
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
|
||||
public:
|
||||
Inner(QWidget *parent, base::lambda<void()> revokeCallback);
|
||||
Inner(QWidget *parent, Fn<void()> revokeCallback);
|
||||
|
||||
protected:
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
@@ -85,7 +85,7 @@ private:
|
||||
int _rowHeight = 0;
|
||||
int _revokeWidth = 0;
|
||||
|
||||
base::lambda<void()> _revokeCallback;
|
||||
Fn<void()> _revokeCallback;
|
||||
mtpRequestId _revokeRequestId = 0;
|
||||
QPointer<ConfirmBox> _weakRevokeConfirmBox;
|
||||
|
||||
@@ -122,9 +122,9 @@ void AddContactBox::prepare() {
|
||||
}
|
||||
updateButtons();
|
||||
|
||||
connect(_first, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_last, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_phone, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
|
||||
connect(_last, &Ui::InputField::submitted, [=] { submit(); });
|
||||
connect(_phone, &Ui::PhoneInput::submitted, [=] { submit(); });
|
||||
|
||||
setDimensions(st::boxWideWidth, st::contactPadding.top() + _first->height() + st::contactSkip + _last->height() + st::contactPhoneSkip + _phone->height() + st::contactPadding.bottom() + st::boxPadding.bottom());
|
||||
}
|
||||
@@ -170,21 +170,21 @@ void AddContactBox::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void AddContactBox::onSubmit() {
|
||||
void AddContactBox::submit() {
|
||||
if (_first->hasFocus()) {
|
||||
_last->setFocus();
|
||||
} else if (_last->hasFocus()) {
|
||||
if (_phone->isEnabled()) {
|
||||
_phone->setFocus();
|
||||
} else {
|
||||
onSave();
|
||||
save();
|
||||
}
|
||||
} else if (_phone->hasFocus()) {
|
||||
onSave();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
void AddContactBox::onSave() {
|
||||
void AddContactBox::save() {
|
||||
if (_addRequest) return;
|
||||
|
||||
auto firstName = TextUtilities::PrepareForSending(_first->getLastText());
|
||||
@@ -274,7 +274,7 @@ void AddContactBox::onSaveUserDone(const MTPcontacts_ImportedContacts &res) {
|
||||
closeBox();
|
||||
}
|
||||
|
||||
void AddContactBox::onRetry() {
|
||||
void AddContactBox::retry() {
|
||||
_addRequest = 0;
|
||||
_contactId = 0;
|
||||
showChildren();
|
||||
@@ -291,9 +291,9 @@ void AddContactBox::onRetry() {
|
||||
void AddContactBox::updateButtons() {
|
||||
clearButtons();
|
||||
if (_retrying) {
|
||||
addButton(langFactory(lng_try_other_contact), [this] { onRetry(); });
|
||||
addButton(langFactory(lng_try_other_contact), [this] { retry(); });
|
||||
} else {
|
||||
addButton(langFactory(_user ? lng_settings_save : lng_add_contact), [this] { onSave(); });
|
||||
addButton(langFactory(_user ? lng_settings_save : lng_add_contact), [this] { save(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
}
|
||||
}
|
||||
@@ -320,20 +320,29 @@ void GroupInfoBox::prepare() {
|
||||
? lng_dlg_new_channel_name
|
||||
: lng_dlg_new_group_name));
|
||||
_title->setMaxLength(kMaxGroupChannelTitle);
|
||||
_title->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_title ->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
|
||||
if (_creating == CreatingGroupChannel) {
|
||||
_description.create(this, st::newGroupDescription, langFactory(lng_create_group_description));
|
||||
_description.create(
|
||||
this,
|
||||
st::newGroupDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_create_group_description));
|
||||
_description->show();
|
||||
_description->setMaxLength(kMaxChannelDescription);
|
||||
_description->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_description->setInstantReplacesEnabled(
|
||||
Global::ReplaceEmojiValue());
|
||||
|
||||
connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
|
||||
connect(_description, SIGNAL(submitted(bool)), this, SLOT(onNext()));
|
||||
connect(_description, SIGNAL(cancelled()), this, SLOT(onClose()));
|
||||
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
|
||||
connect(_description, &Ui::InputField::submitted, [=] { submit(); });
|
||||
connect(_description, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
}
|
||||
|
||||
connect(_title, SIGNAL(submitted(bool)), this, SLOT(onNameSubmit()));
|
||||
connect(_title, &Ui::InputField::submitted, [=] { submitName(); });
|
||||
|
||||
addButton(langFactory(_creating == CreatingGroupChannel ? lng_create_group_create : lng_create_group_next), [this] { onNext(); });
|
||||
addButton(langFactory(_creating == CreatingGroupChannel ? lng_create_group_create : lng_create_group_next), [this] { submit(); });
|
||||
addButton(langFactory(_fromTypeChoose ? lng_create_group_back : lng_cancel), [this] { closeBox(); });
|
||||
|
||||
updateMaxHeight();
|
||||
@@ -364,14 +373,14 @@ void GroupInfoBox::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void GroupInfoBox::onNameSubmit() {
|
||||
void GroupInfoBox::submitName() {
|
||||
if (_title->getLastText().trimmed().isEmpty()) {
|
||||
_title->setFocus();
|
||||
_title->showError();
|
||||
} else if (_description) {
|
||||
_description->setFocus();
|
||||
} else {
|
||||
onNext();
|
||||
submit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,11 +460,15 @@ void GroupInfoBox::createGroup(not_null<PeerListBox*> selectUsersBox, const QStr
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupInfoBox::onNext() {
|
||||
void GroupInfoBox::submit() {
|
||||
if (_creationRequestId) return;
|
||||
|
||||
auto title = TextUtilities::PrepareForSending(_title->getLastText());
|
||||
auto description = _description ? TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString();
|
||||
auto description = _description
|
||||
? TextUtilities::PrepareForSending(
|
||||
_description->getLastText(),
|
||||
TextUtilities::PrepareTextOption::CheckLinks)
|
||||
: QString();
|
||||
if (title.isEmpty()) {
|
||||
_title->setFocus();
|
||||
_title->showError();
|
||||
@@ -551,7 +564,7 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
||||
}).send();
|
||||
}
|
||||
|
||||
void GroupInfoBox::onDescriptionResized() {
|
||||
void GroupInfoBox::descriptionResized() {
|
||||
updateMaxHeight();
|
||||
update();
|
||||
}
|
||||
@@ -579,7 +592,7 @@ SetupChannelBox::SetupChannelBox(QWidget*, ChannelData *channel, bool existing)
|
||||
, _aboutPublicWidth(st::boxWideWidth - st::boxPadding.left() - st::boxButtonPadding.right() - st::newGroupPadding.left() - st::defaultRadio.diameter - st::defaultBoxCheckbox.textPosition.x())
|
||||
, _aboutPublic(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_public_group_about : lng_create_public_channel_about), _defaultOptions, _aboutPublicWidth)
|
||||
, _aboutPrivate(st::defaultTextStyle, lang(channel->isMegagroup() ? lng_create_private_group_about : lng_create_private_channel_about), _defaultOptions, _aboutPublicWidth)
|
||||
, _link(this, st::setupChannelLink, base::lambda<QString()>(), channel->username, true) {
|
||||
, _link(this, st::setupChannelLink, Fn<QString()>(), channel->username, true) {
|
||||
}
|
||||
|
||||
void SetupChannelBox::prepare() {
|
||||
@@ -589,14 +602,14 @@ void SetupChannelBox::prepare() {
|
||||
|
||||
_checkRequestId = MTP::send(MTPchannels_CheckUsername(_channel->inputChannel, MTP_string("preston")), RPCDoneHandlerPtr(), rpcFail(&SetupChannelBox::onFirstCheckFail));
|
||||
|
||||
addButton(langFactory(lng_settings_save), [this] { onSave(); });
|
||||
addButton(langFactory(_existing ? lng_cancel : lng_create_group_skip), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_settings_save), [=] { save(); });
|
||||
addButton(langFactory(_existing ? lng_cancel : lng_create_group_skip), [=] { closeBox(); });
|
||||
|
||||
connect(_link, SIGNAL(changed()), this, SLOT(onChange()));
|
||||
connect(_link, &Ui::MaskedInputField::changed, [=] { handleChange(); });
|
||||
_link->setVisible(_privacyGroup->value() == Privacy::Public);
|
||||
|
||||
_checkTimer.setSingleShot(true);
|
||||
connect(&_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck()));
|
||||
connect(&_checkTimer, &QTimer::timeout, [=] { check(); });
|
||||
|
||||
_privacyGroup->setChangedCallback([this](Privacy value) { privacyChanged(value); });
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::InviteLinkChanged, [this](const Notify::PeerUpdate &update) {
|
||||
@@ -604,11 +617,13 @@ void SetupChannelBox::prepare() {
|
||||
rtlupdate(_invitationLink);
|
||||
}
|
||||
}));
|
||||
subscribe(boxClosing, [this] {
|
||||
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (!_existing) {
|
||||
AddParticipantsBoxController::Start(_channel);
|
||||
}
|
||||
});
|
||||
}, lifetime());
|
||||
|
||||
updateMaxHeight();
|
||||
}
|
||||
|
||||
@@ -635,7 +650,7 @@ void SetupChannelBox::keyPressEvent(QKeyEvent *e) {
|
||||
_link->setFocus();
|
||||
_link->showError();
|
||||
} else {
|
||||
onSave();
|
||||
save();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -724,7 +739,7 @@ void SetupChannelBox::updateSelected(const QPoint &cursorGlobalPosition) {
|
||||
}
|
||||
}
|
||||
|
||||
void SetupChannelBox::onSave() {
|
||||
void SetupChannelBox::save() {
|
||||
if (_privacyGroup->value() == Privacy::Private) {
|
||||
if (_existing) {
|
||||
_sentUsername = QString();
|
||||
@@ -747,7 +762,7 @@ void SetupChannelBox::onSave() {
|
||||
_saveRequestId = MTP::send(MTPchannels_UpdateUsername(_channel->inputChannel, MTP_string(_sentUsername)), rpcDone(&SetupChannelBox::onUpdateDone), rpcFail(&SetupChannelBox::onUpdateFail));
|
||||
}
|
||||
|
||||
void SetupChannelBox::onChange() {
|
||||
void SetupChannelBox::handleChange() {
|
||||
QString name = _link->text().trimmed();
|
||||
if (name.isEmpty()) {
|
||||
if (!_errorText.isEmpty() || !_goodText.isEmpty()) {
|
||||
@@ -784,7 +799,7 @@ void SetupChannelBox::onChange() {
|
||||
}
|
||||
}
|
||||
|
||||
void SetupChannelBox::onCheck() {
|
||||
void SetupChannelBox::check() {
|
||||
if (_checkRequestId) {
|
||||
MTP::cancel(_checkRequestId);
|
||||
}
|
||||
@@ -804,10 +819,10 @@ void SetupChannelBox::privacyChanged(Privacy value) {
|
||||
if (value == Privacy::Public) {
|
||||
if (_tooMuchUsernames) {
|
||||
_privacyGroup->setValue(Privacy::Private);
|
||||
Ui::show(Box<RevokePublicLinkBox>(base::lambda_guarded(this, [this] {
|
||||
Ui::show(Box<RevokePublicLinkBox>(crl::guard(this, [this] {
|
||||
_tooMuchUsernames = false;
|
||||
_privacyGroup->setValue(Privacy::Public);
|
||||
onCheck();
|
||||
check();
|
||||
})), LayerOption::KeepOther);
|
||||
return;
|
||||
}
|
||||
@@ -943,16 +958,16 @@ void EditNameBox::prepare() {
|
||||
newHeight += st::boxPadding.bottom() + st::contactPadding.bottom();
|
||||
setDimensions(st::boxWideWidth, newHeight);
|
||||
|
||||
addButton(langFactory(lng_settings_save), [this] { save(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_settings_save), [=] { save(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
if (_invertOrder) {
|
||||
setTabOrder(_last, _first);
|
||||
}
|
||||
_first->setMaxLength(kMaxGroupChannelTitle);
|
||||
_last->setMaxLength(kMaxGroupChannelTitle);
|
||||
|
||||
connect(_first, &Ui::InputField::submitted, this, [this] { submit(); });
|
||||
connect(_last, &Ui::InputField::submitted, this, [this] { submit(); });
|
||||
connect(_first, &Ui::InputField::submitted, [=] { submit(); });
|
||||
connect(_last, &Ui::InputField::submitted, [=] { submit(); });
|
||||
}
|
||||
|
||||
void EditNameBox::setInnerFocus() {
|
||||
@@ -1052,7 +1067,12 @@ bool EditNameBox::saveSelfFail(const RPCError &error) {
|
||||
EditBioBox::EditBioBox(QWidget*, not_null<UserData*> self) : BoxContent()
|
||||
, _dynamicFieldStyle(CreateBioFieldStyle())
|
||||
, _self(self)
|
||||
, _bio(this, _dynamicFieldStyle, langFactory(lng_bio_placeholder), _self->about())
|
||||
, _bio(
|
||||
this,
|
||||
_dynamicFieldStyle,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_bio_placeholder),
|
||||
_self->about())
|
||||
, _countdown(this, QString(), Ui::FlatLabel::InitType::Simple, st::editBioCountdownLabel)
|
||||
, _about(this, lang(lng_bio_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel) {
|
||||
}
|
||||
@@ -1063,13 +1083,15 @@ void EditBioBox::prepare() {
|
||||
addButton(langFactory(lng_settings_save), [this] { save(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
_bio->setMaxLength(kMaxBioLength);
|
||||
_bio->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_bio->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
auto cursor = _bio->textCursor();
|
||||
cursor.setPosition(_bio->getLastText().size());
|
||||
_bio->setTextCursor(cursor);
|
||||
connect(_bio, &Ui::InputArea::submitted, this, [this](bool ctrlShiftEnter) { save(); });
|
||||
connect(_bio, &Ui::InputArea::resized, this, [this] { updateMaxHeight(); });
|
||||
connect(_bio, &Ui::InputArea::changed, this, [this] { handleBioUpdated(); });
|
||||
connect(_bio, &Ui::InputField::submitted, [=] { save(); });
|
||||
connect(_bio, &Ui::InputField::resized, [=] { updateMaxHeight(); });
|
||||
connect(_bio, &Ui::InputField::changed, [=] { handleBioUpdated(); });
|
||||
_bio->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_bio->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
handleBioUpdated();
|
||||
updateMaxHeight();
|
||||
}
|
||||
@@ -1122,7 +1144,12 @@ void EditBioBox::save() {
|
||||
EditChannelBox::EditChannelBox(QWidget*, not_null<ChannelData*> channel)
|
||||
: _channel(channel)
|
||||
, _title(this, st::defaultInputField, langFactory(_channel->isMegagroup() ? lng_dlg_new_group_name : lng_dlg_new_channel_name), _channel->name)
|
||||
, _description(this, st::newGroupDescription, langFactory(lng_create_group_description), _channel->about())
|
||||
, _description(
|
||||
this,
|
||||
st::newGroupDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_create_group_description),
|
||||
_channel->about())
|
||||
, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature(), st::defaultBoxCheckbox)
|
||||
, _inviteGroup(std::make_shared<Ui::RadioenumGroup<Invites>>(channel->anyoneCanAddMembers() ? Invites::Everybody : Invites::OnlyAdmins))
|
||||
, _inviteEverybody(this, _inviteGroup, Invites::Everybody, lang(lng_edit_group_invites_everybody))
|
||||
@@ -1133,7 +1160,7 @@ EditChannelBox::EditChannelBox(QWidget*, not_null<ChannelData*> channel)
|
||||
void EditChannelBox::prepare() {
|
||||
setTitle(langFactory(_channel->isMegagroup() ? lng_edit_group : lng_edit_channel_title));
|
||||
|
||||
addButton(langFactory(lng_settings_save), [this] { onSave(); });
|
||||
addButton(langFactory(lng_settings_save), [this] { save(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
|
||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::NameChanged, [this](const Notify::PeerUpdate &update) {
|
||||
@@ -1145,13 +1172,17 @@ void EditChannelBox::prepare() {
|
||||
setMouseTracking(true);
|
||||
|
||||
_title->setMaxLength(kMaxGroupChannelTitle);
|
||||
_title->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_title->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
_description->setMaxLength(kMaxChannelDescription);
|
||||
_description->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_description->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
|
||||
connect(_description, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
|
||||
connect(_description, SIGNAL(submitted(bool)), this, SLOT(onSave()));
|
||||
connect(_description, SIGNAL(cancelled()), this, SLOT(onClose()));
|
||||
connect(_description, &Ui::InputField::resized, [=] { descriptionResized(); });
|
||||
connect(_description, &Ui::InputField::submitted, [=] { save(); });
|
||||
connect(_description, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
|
||||
connect(_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink()));
|
||||
_publicLink->addClickHandler([=] { setupPublicLink(); });
|
||||
_publicLink->setVisible(_channel->canEditUsername());
|
||||
_sign->setVisible(canEditSignatures());
|
||||
_inviteEverybody->setVisible(canEditInvites());
|
||||
@@ -1167,7 +1198,7 @@ void EditChannelBox::setInnerFocus() {
|
||||
void EditChannelBox::keyPressEvent(QKeyEvent *e) {
|
||||
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||
if (_title->hasFocus()) {
|
||||
onSave();
|
||||
save();
|
||||
}
|
||||
} else {
|
||||
BoxContent::keyPressEvent(e);
|
||||
@@ -1179,7 +1210,7 @@ void EditChannelBox::handleChannelNameChange() {
|
||||
_sign->setChecked(_channel->addsSignature());
|
||||
}
|
||||
|
||||
void EditChannelBox::onDescriptionResized() {
|
||||
void EditChannelBox::descriptionResized() {
|
||||
updateMaxHeight();
|
||||
update();
|
||||
}
|
||||
@@ -1243,7 +1274,7 @@ void EditChannelBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditChannelBox::onSave() {
|
||||
void EditChannelBox::save() {
|
||||
if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId || _saveInvitesRequestId) return;
|
||||
|
||||
auto title = TextUtilities::PrepareForSending(_title->getLastText());
|
||||
@@ -1262,7 +1293,7 @@ void EditChannelBox::onSave() {
|
||||
}
|
||||
}
|
||||
|
||||
void EditChannelBox::onPublicLink() {
|
||||
void EditChannelBox::setupPublicLink() {
|
||||
Ui::show(
|
||||
Box<SetupChannelBox>(_channel, true),
|
||||
LayerOption::KeepOther);
|
||||
@@ -1358,7 +1389,7 @@ void EditChannelBox::onSaveInvitesDone(const MTPUpdates &result) {
|
||||
closeBox();
|
||||
}
|
||||
|
||||
RevokePublicLinkBox::Inner::Inner(QWidget *parent, base::lambda<void()> revokeCallback) : TWidget(parent)
|
||||
RevokePublicLinkBox::Inner::Inner(QWidget *parent, Fn<void()> revokeCallback) : TWidget(parent)
|
||||
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
||||
, _revokeWidth(st::normalFont->width(lang(lng_channels_too_much_public_revoke)))
|
||||
, _revokeCallback(std::move(revokeCallback)) {
|
||||
@@ -1394,7 +1425,7 @@ RevokePublicLinkBox::Inner::Inner(QWidget *parent, base::lambda<void()> revokeCa
|
||||
}).send();
|
||||
}
|
||||
|
||||
RevokePublicLinkBox::RevokePublicLinkBox(QWidget*, base::lambda<void()> revokeCallback)
|
||||
RevokePublicLinkBox::RevokePublicLinkBox(QWidget*, Fn<void()> revokeCallback)
|
||||
: _aboutRevoke(this, lang(lng_channels_too_much_public_about), Ui::FlatLabel::InitType::Simple, st::aboutRevokePublicLabel)
|
||||
, _revokeCallback(std::move(revokeCallback)) {
|
||||
}
|
||||
@@ -1453,7 +1484,7 @@ void RevokePublicLinkBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||
auto text_method = pressed->isMegagroup() ? lng_channels_too_much_public_revoke_confirm_group : lng_channels_too_much_public_revoke_confirm_channel;
|
||||
auto text = text_method(lt_link, Messenger::Instance().createInternalLink(pressed->userName()), lt_group, pressed->name);
|
||||
auto confirmText = lang(lng_channels_too_much_public_revoke);
|
||||
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, base::lambda_guarded(this, [this, pressed]() {
|
||||
_weakRevokeConfirmBox = Ui::show(Box<ConfirmBox>(text, confirmText, crl::guard(this, [this, pressed]() {
|
||||
if (_revokeRequestId) return;
|
||||
_revokeRequestId = request(MTPchannels_UpdateUsername(pressed->asChannel()->inputChannel, MTP_string(""))).done([this](const MTPBool &result) {
|
||||
if (_weakRevokeConfirmBox) {
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Ui {
|
||||
class FlatLabel;
|
||||
class InputField;
|
||||
class PhoneInput;
|
||||
class InputArea;
|
||||
class UsernameInput;
|
||||
class Checkbox;
|
||||
template <typename Enum>
|
||||
@@ -37,8 +36,6 @@ enum class PeerFloodType {
|
||||
QString PeerFloodErrorText(PeerFloodType type);
|
||||
|
||||
class AddContactBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AddContactBox(QWidget*, QString fname = QString(), QString lname = QString(), QString phone = QString());
|
||||
AddContactBox(QWidget*, UserData *user);
|
||||
@@ -51,12 +48,10 @@ protected:
|
||||
|
||||
void setInnerFocus() override;
|
||||
|
||||
private slots:
|
||||
void onSubmit();
|
||||
void onSave();
|
||||
void onRetry();
|
||||
|
||||
private:
|
||||
void submit();
|
||||
void retry();
|
||||
void save();
|
||||
void updateButtons();
|
||||
void onImportDone(const MTPcontacts_ImportedContacts &res);
|
||||
|
||||
@@ -80,8 +75,6 @@ private:
|
||||
};
|
||||
|
||||
class GroupInfoBox : public BoxContent, private MTP::Sender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GroupInfoBox(QWidget*, CreatingGroupType creating, bool fromTypeChoose);
|
||||
|
||||
@@ -91,18 +84,13 @@ protected:
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void onNext();
|
||||
void onNameSubmit();
|
||||
void onDescriptionResized();
|
||||
void onClose() {
|
||||
closeBox();
|
||||
}
|
||||
|
||||
private:
|
||||
void createChannel(const QString &title, const QString &description);
|
||||
void createGroup(not_null<PeerListBox*> selectUsersBox, const QString &title, const std::vector<not_null<PeerData*>> &users);
|
||||
void submitName();
|
||||
void submit();
|
||||
|
||||
void descriptionResized();
|
||||
void updateMaxHeight();
|
||||
void updateSelected(const QPoint &cursorGlobalPosition);
|
||||
|
||||
@@ -111,7 +99,7 @@ private:
|
||||
|
||||
object_ptr<Ui::UserpicButton> _photo = { nullptr };
|
||||
object_ptr<Ui::InputField> _title = { nullptr };
|
||||
object_ptr<Ui::InputArea> _description = { nullptr };
|
||||
object_ptr<Ui::InputField> _description = { nullptr };
|
||||
|
||||
// group / channel creation
|
||||
mtpRequestId _creationRequestId = 0;
|
||||
@@ -120,8 +108,6 @@ private:
|
||||
};
|
||||
|
||||
class SetupChannelBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SetupChannelBox(QWidget*, ChannelData *channel, bool existing = false);
|
||||
|
||||
@@ -137,11 +123,6 @@ protected:
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void onSave();
|
||||
void onChange();
|
||||
void onCheck();
|
||||
|
||||
private:
|
||||
enum class Privacy {
|
||||
Public,
|
||||
@@ -150,6 +131,9 @@ private:
|
||||
void privacyChanged(Privacy value);
|
||||
void updateSelected(const QPoint &cursorGlobalPosition);
|
||||
void showAddContactsToChannelBox() const;
|
||||
void handleChange();
|
||||
void check();
|
||||
void save();
|
||||
|
||||
void onUpdateDone(const MTPBool &result);
|
||||
bool onUpdateFail(const RPCError &error);
|
||||
@@ -231,7 +215,7 @@ private:
|
||||
style::InputField _dynamicFieldStyle;
|
||||
not_null<UserData*> _self;
|
||||
|
||||
object_ptr<Ui::InputArea> _bio;
|
||||
object_ptr<Ui::InputField> _bio;
|
||||
object_ptr<Ui::FlatLabel> _countdown;
|
||||
object_ptr<Ui::FlatLabel> _about;
|
||||
mtpRequestId _requestId = 0;
|
||||
@@ -240,8 +224,6 @@ private:
|
||||
};
|
||||
|
||||
class EditChannelBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EditChannelBox(QWidget*, not_null<ChannelData*> channel);
|
||||
|
||||
@@ -253,19 +235,14 @@ protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void onSave();
|
||||
void onDescriptionResized();
|
||||
void onPublicLink();
|
||||
void onClose() {
|
||||
closeBox();
|
||||
}
|
||||
|
||||
private:
|
||||
void updateMaxHeight();
|
||||
bool canEditSignatures() const;
|
||||
bool canEditInvites() const;
|
||||
void handleChannelNameChange();
|
||||
void descriptionResized();
|
||||
void setupPublicLink();
|
||||
void save();
|
||||
|
||||
void onSaveTitleDone(const MTPUpdates &result);
|
||||
void onSaveDescriptionDone(const MTPBool &result);
|
||||
@@ -280,7 +257,7 @@ private:
|
||||
not_null<ChannelData*> _channel;
|
||||
|
||||
object_ptr<Ui::InputField> _title;
|
||||
object_ptr<Ui::InputArea> _description;
|
||||
object_ptr<Ui::InputField> _description;
|
||||
object_ptr<Ui::Checkbox> _sign;
|
||||
|
||||
enum class Invites {
|
||||
@@ -304,7 +281,7 @@ private:
|
||||
|
||||
class RevokePublicLinkBox : public BoxContent, public RPCSender {
|
||||
public:
|
||||
RevokePublicLinkBox(QWidget*, base::lambda<void()> revokeCallback);
|
||||
RevokePublicLinkBox(QWidget*, Fn<void()> revokeCallback);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -318,6 +295,6 @@ private:
|
||||
QPointer<Inner> _inner;
|
||||
|
||||
int _innerTop = 0;
|
||||
base::lambda<void()> _revokeCallback;
|
||||
Fn<void()> _revokeCallback;
|
||||
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ class BackgroundBox::Inner : public TWidget, public RPCSender, private base::Sub
|
||||
public:
|
||||
Inner(QWidget *parent);
|
||||
|
||||
void setBackgroundChosenCallback(base::lambda<void(int index)> callback) {
|
||||
void setBackgroundChosenCallback(Fn<void(int index)> callback) {
|
||||
_backgroundChosenCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ private:
|
||||
void gotWallpapers(const MTPVector<MTPWallPaper> &result);
|
||||
void updateWallpapers();
|
||||
|
||||
base::lambda<void(int index)> _backgroundChosenCallback;
|
||||
Fn<void(int index)> _backgroundChosenCallback;
|
||||
|
||||
int _bgCount = 0;
|
||||
int _rows = 0;
|
||||
|
||||
@@ -119,6 +119,7 @@ boxLabel: FlatLabel(defaultFlatLabel) {
|
||||
}
|
||||
boxDividerLabel: FlatLabel(boxLabel) {
|
||||
minWidth: 245px;
|
||||
align: align(topleft);
|
||||
textFg: windowSubTextFg;
|
||||
style: defaultTextStyle;
|
||||
}
|
||||
@@ -781,3 +782,12 @@ proxyDropdownDownPosition: point(-2px, 35px);
|
||||
proxyDropdownUpPosition: point(-2px, 20px);
|
||||
|
||||
proxyAboutPadding: margins(22px, 7px, 22px, 14px);
|
||||
proxyAboutSponsorPadding: margins(22px, 7px, 22px, 0px);
|
||||
|
||||
markdownLinkFieldPadding: margins(22px, 0px, 22px, 10px);
|
||||
|
||||
termsContent: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 285px;
|
||||
}
|
||||
termsPadding: margins(23px, 4px, 16px, 16px);
|
||||
termsAgePadding: margins(23px, 16px, 16px, 0px);
|
||||
|
||||
@@ -192,7 +192,7 @@ public:
|
||||
return st::calendarPadding.top() + innerHeight + st::calendarPadding.bottom();
|
||||
}
|
||||
|
||||
void setDateChosenCallback(base::lambda<void(QDate)> callback) {
|
||||
void setDateChosenCallback(Fn<void(QDate)> callback) {
|
||||
_dateChosenCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ private:
|
||||
|
||||
std::map<int, std::unique_ptr<Ui::RippleAnimation>> _ripples;
|
||||
|
||||
base::lambda<void(QDate)> _dateChosenCallback;
|
||||
Fn<void(QDate)> _dateChosenCallback;
|
||||
|
||||
static constexpr auto kEmptySelection = -kDaysInWeek;
|
||||
int _selected = kEmptySelection;
|
||||
@@ -440,7 +440,7 @@ void CalendarBox::Title::paintEvent(QPaintEvent *e) {
|
||||
p.drawTextLeft((width() - _textWidth) / 2, (height() - st::calendarTitleFont->height) / 2, width(), _text, _textWidth);
|
||||
}
|
||||
|
||||
CalendarBox::CalendarBox(QWidget*, QDate month, QDate highlighted, base::lambda<void(QDate date)> callback)
|
||||
CalendarBox::CalendarBox(QWidget*, QDate month, QDate highlighted, Fn<void(QDate date)> callback)
|
||||
: _context(std::make_unique<Context>(month, highlighted))
|
||||
, _inner(this, _context.get())
|
||||
, _title(this, _context.get())
|
||||
|
||||
@@ -15,7 +15,7 @@ class IconButton;
|
||||
|
||||
class CalendarBox : public BoxContent {
|
||||
public:
|
||||
CalendarBox(QWidget*, QDate month, QDate highlighted, base::lambda<void(QDate date)> callback);
|
||||
CalendarBox(QWidget*, QDate month, QDate highlighted, Fn<void(QDate date)> callback);
|
||||
|
||||
void setMinDate(QDate date);
|
||||
void setMaxDate(QDate date);
|
||||
@@ -44,6 +44,6 @@ private:
|
||||
object_ptr<Ui::IconButton> _previous;
|
||||
object_ptr<Ui::IconButton> _next;
|
||||
|
||||
base::lambda<void(QDate date)> _callback;
|
||||
Fn<void(QDate date)> _callback;
|
||||
|
||||
};
|
||||
|
||||
@@ -7,9 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/change_phone_box.h"
|
||||
|
||||
#include <rpl/filter.h>
|
||||
#include <rpl/mappers.h>
|
||||
#include <rpl/take.h>
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
@@ -128,7 +125,7 @@ void ChangePhoneBox::EnterPhone::prepare() {
|
||||
|
||||
_phone->resize(st::boxWidth - 2 * st::boxPadding.left(), _phone->height());
|
||||
_phone->moveToLeft(st::boxPadding.left(), st::boxLittleSkip);
|
||||
connect(_phone, &Ui::PhoneInput::submitted, this, [this] { submit(); });
|
||||
connect(_phone, &Ui::PhoneInput::submitted, [=] { submit(); });
|
||||
|
||||
auto description = object_ptr<Ui::FlatLabel>(this, lang(lng_change_phone_new_description), Ui::FlatLabel::InitType::Simple, st::changePhoneLabel);
|
||||
auto errorSkip = st::boxLittleSkip + st::changePhoneError.style.font->height;
|
||||
@@ -147,9 +144,9 @@ void ChangePhoneBox::EnterPhone::submit() {
|
||||
hideError();
|
||||
|
||||
auto phoneNumber = _phone->getLastText().trimmed();
|
||||
_requestId = MTP::send(MTPaccount_SendChangePhoneCode(MTP_flags(0), MTP_string(phoneNumber), MTP_bool(false)), rpcDone(base::lambda_guarded(this, [this, phoneNumber](const MTPauth_SentCode &result) {
|
||||
_requestId = MTP::send(MTPaccount_SendChangePhoneCode(MTP_flags(0), MTP_string(phoneNumber), MTP_bool(false)), rpcDone(crl::guard(this, [this, phoneNumber](const MTPauth_SentCode &result) {
|
||||
return sendPhoneDone(phoneNumber, result);
|
||||
})), rpcFail(base::lambda_guarded(this, [this, phoneNumber](const RPCError &error) {
|
||||
})), rpcFail(crl::guard(this, [this, phoneNumber](const RPCError &error) {
|
||||
return sendPhoneFail(phoneNumber, error);
|
||||
})));
|
||||
}
|
||||
@@ -216,7 +213,7 @@ ChangePhoneBox::EnterCode::EnterCode(QWidget*, const QString &phone, const QStri
|
||||
, _hash(hash)
|
||||
, _codeLength(codeLength)
|
||||
, _callTimeout(callTimeout)
|
||||
, _call(this, [this] { sendCall(); }, [this] { updateCall(); }) {
|
||||
, _call([this] { sendCall(); }, [this] { updateCall(); }) {
|
||||
}
|
||||
|
||||
void ChangePhoneBox::EnterCode::prepare() {
|
||||
@@ -228,12 +225,12 @@ void ChangePhoneBox::EnterCode::prepare() {
|
||||
|
||||
auto phoneValue = QString();
|
||||
_code.create(this, st::defaultInputField, langFactory(lng_change_phone_code_title), phoneValue);
|
||||
_code->setAutoSubmit(_codeLength, [this] { submit(); });
|
||||
_code->setChangedCallback([this] { hideError(); });
|
||||
_code->setAutoSubmit(_codeLength, [=] { submit(); });
|
||||
_code->setChangedCallback([=] { hideError(); });
|
||||
|
||||
_code->resize(st::boxWidth - 2 * st::boxPadding.left(), _code->height());
|
||||
_code->moveToLeft(st::boxPadding.left(), description->bottomNoMargins());
|
||||
connect(_code, &Ui::InputField::submitted, this, [this] { submit(); });
|
||||
connect(_code, &Ui::InputField::submitted, [=] { submit(); });
|
||||
|
||||
setDimensions(st::boxWidth, countHeight());
|
||||
|
||||
@@ -242,8 +239,8 @@ void ChangePhoneBox::EnterCode::prepare() {
|
||||
updateCall();
|
||||
}
|
||||
|
||||
addButton(langFactory(lng_change_phone_new_submit), [this] { submit(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_change_phone_new_submit), [=] { submit(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
}
|
||||
|
||||
int ChangePhoneBox::EnterCode::countHeight() {
|
||||
@@ -268,13 +265,13 @@ void ChangePhoneBox::EnterCode::submit() {
|
||||
Ui::hideLayer();
|
||||
}
|
||||
Ui::Toast::Show(lang(lng_change_phone_success));
|
||||
}), rpcFail(base::lambda_guarded(this, [this](const RPCError &error) {
|
||||
}), rpcFail(crl::guard(this, [this](const RPCError &error) {
|
||||
return sendCodeFail(error);
|
||||
})));
|
||||
}
|
||||
|
||||
void ChangePhoneBox::EnterCode::sendCall() {
|
||||
MTP::send(MTPauth_ResendCode(MTP_string(_phone), MTP_string(_hash)), rpcDone(base::lambda_guarded(this, [this] {
|
||||
MTP::send(MTPauth_ResendCode(MTP_string(_phone), MTP_string(_hash)), rpcDone(crl::guard(this, [this] {
|
||||
_call.callDone();
|
||||
})));
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ TextParseOptions _confirmBoxTextOptions = {
|
||||
Qt::LayoutDirectionAuto, // dir
|
||||
};
|
||||
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, base::lambda_once<void()> confirmedCallback, base::lambda_once<void()> cancelledCallback)
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
|
||||
: _confirmText(lang(lng_box_ok))
|
||||
, _cancelText(lang(lng_cancel))
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
@@ -43,7 +43,7 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, base::lambda_once<void()>
|
||||
init(text);
|
||||
}
|
||||
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, base::lambda_once<void()> confirmedCallback, base::lambda_once<void()> cancelledCallback)
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(lang(lng_cancel))
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
@@ -53,7 +53,17 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
|
||||
init(text);
|
||||
}
|
||||
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, base::lambda_once<void()> confirmedCallback, base::lambda_once<void()> cancelledCallback)
|
||||
ConfirmBox::ConfirmBox(QWidget*, const TextWithEntities &text, const QString &confirmText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(lang(lng_cancel))
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right())
|
||||
, _confirmedCallback(std::move(confirmedCallback))
|
||||
, _cancelledCallback(std::move(cancelledCallback)) {
|
||||
init(text);
|
||||
}
|
||||
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(lang(lng_cancel))
|
||||
, _confirmStyle(confirmStyle)
|
||||
@@ -63,7 +73,7 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
|
||||
init(text);
|
||||
}
|
||||
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, base::lambda_once<void()> confirmedCallback, base::lambda_once<void()> cancelledCallback)
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(cancelText)
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
@@ -73,7 +83,7 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
|
||||
init(text);
|
||||
}
|
||||
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, base::lambda_once<void()> confirmedCallback, base::lambda_once<void()> cancelledCallback)
|
||||
ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, FnMut<void()> confirmedCallback, FnMut<void()> cancelledCallback)
|
||||
: _confirmText(confirmText)
|
||||
, _cancelText(cancelText)
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
@@ -83,7 +93,7 @@ ConfirmBox::ConfirmBox(QWidget*, const QString &text, const QString &confirmText
|
||||
init(text);
|
||||
}
|
||||
|
||||
ConfirmBox::ConfirmBox(const InformBoxTag &, const QString &text, const QString &doneText, base::lambda<void()> closedCallback)
|
||||
ConfirmBox::ConfirmBox(const InformBoxTag &, const QString &text, const QString &doneText, Fn<void()> closedCallback)
|
||||
: _confirmText(doneText)
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
, _informative(true)
|
||||
@@ -93,8 +103,18 @@ ConfirmBox::ConfirmBox(const InformBoxTag &, const QString &text, const QString
|
||||
init(text);
|
||||
}
|
||||
|
||||
base::lambda_once<void()> ConfirmBox::generateInformCallback(base::lambda<void()> closedCallback) {
|
||||
return base::lambda_guarded(this, [this, closedCallback] {
|
||||
ConfirmBox::ConfirmBox(const InformBoxTag &, const TextWithEntities &text, const QString &doneText, Fn<void()> closedCallback)
|
||||
: _confirmText(doneText)
|
||||
, _confirmStyle(st::defaultBoxButton)
|
||||
, _informative(true)
|
||||
, _text(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right())
|
||||
, _confirmedCallback(generateInformCallback(closedCallback))
|
||||
, _cancelledCallback(generateInformCallback(closedCallback)) {
|
||||
init(text);
|
||||
}
|
||||
|
||||
FnMut<void()> ConfirmBox::generateInformCallback(Fn<void()> closedCallback) {
|
||||
return crl::guard(this, [this, closedCallback] {
|
||||
closeBox();
|
||||
if (closedCallback) {
|
||||
closedCallback();
|
||||
@@ -106,22 +126,38 @@ void ConfirmBox::init(const QString &text) {
|
||||
_text.setText(st::boxLabelStyle, text, _informative ? _confirmBoxTextOptions : _textPlainOptions);
|
||||
}
|
||||
|
||||
void ConfirmBox::init(const TextWithEntities &text) {
|
||||
_text.setMarkedText(st::boxLabelStyle, text, _confirmBoxTextOptions);
|
||||
}
|
||||
|
||||
void ConfirmBox::prepare() {
|
||||
addButton([this] { return _confirmText; }, [this] { confirmed(); }, _confirmStyle);
|
||||
if (!_informative) {
|
||||
addButton([this] { return _cancelText; }, [this] { _cancelled = true; closeBox(); });
|
||||
}
|
||||
subscribe(boxClosing, [this] {
|
||||
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (!_confirmed && (!_strictCancel || _cancelled) && _cancelledCallback) {
|
||||
_cancelledCallback();
|
||||
}
|
||||
});
|
||||
}, lifetime());
|
||||
|
||||
textUpdated();
|
||||
}
|
||||
|
||||
void ConfirmBox::setMaxLineCount(int count) {
|
||||
if (_maxLineCount != count) {
|
||||
_maxLineCount = count;
|
||||
textUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
void ConfirmBox::textUpdated() {
|
||||
_textWidth = st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right();
|
||||
_textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxLabelStyle.lineHeight);
|
||||
_textHeight = _text.countHeight(_textWidth);
|
||||
if (_maxLineCount > 0) {
|
||||
accumulate_min(_textHeight, _maxLineCount * st::boxLabelStyle.lineHeight);
|
||||
}
|
||||
setDimensions(st::boxWidth, st::boxPadding.top() + _textHeight + st::boxPadding.bottom());
|
||||
|
||||
setMouseTracking(_text.hasLinks());
|
||||
@@ -198,13 +234,23 @@ void ConfirmBox::paintEvent(QPaintEvent *e) {
|
||||
|
||||
// draw box title / text
|
||||
p.setPen(st::boxTextFg);
|
||||
_text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), 16, style::al_left);
|
||||
if (_maxLineCount > 0) {
|
||||
_text.drawLeftElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), _maxLineCount, style::al_left);
|
||||
} else {
|
||||
_text.drawLeft(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, width(), style::al_left);
|
||||
}
|
||||
}
|
||||
|
||||
InformBox::InformBox(QWidget*, const QString &text, base::lambda<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, lang(lng_box_ok), std::move(closedCallback)) {
|
||||
InformBox::InformBox(QWidget*, const QString &text, Fn<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, lang(lng_box_ok), std::move(closedCallback)) {
|
||||
}
|
||||
|
||||
InformBox::InformBox(QWidget*, const QString &text, const QString &doneText, base::lambda<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, doneText, std::move(closedCallback)) {
|
||||
InformBox::InformBox(QWidget*, const QString &text, const QString &doneText, Fn<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, doneText, std::move(closedCallback)) {
|
||||
}
|
||||
|
||||
InformBox::InformBox(QWidget*, const TextWithEntities &text, Fn<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, lang(lng_box_ok), std::move(closedCallback)) {
|
||||
}
|
||||
|
||||
InformBox::InformBox(QWidget*, const TextWithEntities &text, const QString &doneText, Fn<void()> closedCallback) : ConfirmBox(ConfirmBox::InformBoxTag(), text, doneText, std::move(closedCallback)) {
|
||||
}
|
||||
|
||||
MaxInviteBox::MaxInviteBox(QWidget*, not_null<ChannelData*> channel) : BoxContent()
|
||||
|
||||
@@ -18,11 +18,12 @@ class EmptyUserpic;
|
||||
class InformBox;
|
||||
class ConfirmBox : public BoxContent, public ClickHandlerHost {
|
||||
public:
|
||||
ConfirmBox(QWidget*, const QString &text, base::lambda_once<void()> confirmedCallback = base::lambda_once<void()>(), base::lambda_once<void()> cancelledCallback = base::lambda_once<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, base::lambda_once<void()> confirmedCallback = base::lambda_once<void()>(), base::lambda_once<void()> cancelledCallback = base::lambda_once<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, base::lambda_once<void()> confirmedCallback = base::lambda_once<void()>(), base::lambda_once<void()> cancelledCallback = base::lambda_once<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, base::lambda_once<void()> confirmedCallback = base::lambda_once<void()>(), base::lambda_once<void()> cancelledCallback = base::lambda_once<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, base::lambda_once<void()> confirmedCallback = base::lambda_once<void()>(), base::lambda_once<void()> cancelledCallback = base::lambda_once<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const QString &cancelText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const QString &text, const QString &confirmText, const style::RoundButton &confirmStyle, const QString &cancelText, FnMut<void()> confirmedCallback = FnMut<void()>(), FnMut<void()> cancelledCallback = FnMut<void()>());
|
||||
ConfirmBox(QWidget*, const TextWithEntities &text, const QString &confirmText, FnMut<void()> confirmedCallback = nullptr, FnMut<void()> cancelledCallback = nullptr);
|
||||
|
||||
void updateLink();
|
||||
|
||||
@@ -31,6 +32,8 @@ public:
|
||||
_strictCancel = strictCancel;
|
||||
}
|
||||
|
||||
void setMaxLineCount(int count);
|
||||
|
||||
// ClickHandlerHost interface
|
||||
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
|
||||
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
|
||||
@@ -48,13 +51,16 @@ protected:
|
||||
private:
|
||||
struct InformBoxTag {
|
||||
};
|
||||
ConfirmBox(const InformBoxTag &, const QString &text, const QString &doneText, base::lambda<void()> closedCallback);
|
||||
base::lambda_once<void()> generateInformCallback(base::lambda<void()> closedCallback);
|
||||
ConfirmBox(const InformBoxTag &, const QString &text, const QString &doneText, Fn<void()> closedCallback);
|
||||
ConfirmBox(const InformBoxTag &, const TextWithEntities &text, const QString &doneText, Fn<void()> closedCallback);
|
||||
FnMut<void()> generateInformCallback(Fn<void()> closedCallback);
|
||||
friend class InformBox;
|
||||
|
||||
void confirmed();
|
||||
void init(const QString &text);
|
||||
void init(const TextWithEntities &text);
|
||||
void textUpdated();
|
||||
void updateHover();
|
||||
|
||||
QString _confirmText;
|
||||
QString _cancelText;
|
||||
@@ -64,23 +70,24 @@ private:
|
||||
Text _text;
|
||||
int _textWidth = 0;
|
||||
int _textHeight = 0;
|
||||
|
||||
void updateHover();
|
||||
int _maxLineCount = 16;
|
||||
|
||||
QPoint _lastMousePos;
|
||||
|
||||
bool _confirmed = false;
|
||||
bool _cancelled = false;
|
||||
bool _strictCancel = false;
|
||||
base::lambda_once<void()> _confirmedCallback;
|
||||
base::lambda_once<void()> _cancelledCallback;
|
||||
FnMut<void()> _confirmedCallback;
|
||||
FnMut<void()> _cancelledCallback;
|
||||
|
||||
};
|
||||
|
||||
class InformBox : public ConfirmBox {
|
||||
public:
|
||||
InformBox(QWidget*, const QString &text, base::lambda<void()> closedCallback = base::lambda<void()>());
|
||||
InformBox(QWidget*, const QString &text, const QString &doneText, base::lambda<void()> closedCallback = base::lambda<void()>());
|
||||
InformBox(QWidget*, const QString &text, Fn<void()> closedCallback = nullptr);
|
||||
InformBox(QWidget*, const QString &text, const QString &doneText, Fn<void()> closedCallback = nullptr);
|
||||
InformBox(QWidget*, const TextWithEntities &text, Fn<void()> closedCallback = nullptr);
|
||||
InformBox(QWidget*, const TextWithEntities &text, const QString &doneText, Fn<void()> closedCallback = nullptr);
|
||||
|
||||
};
|
||||
|
||||
@@ -166,7 +173,7 @@ public:
|
||||
bool suggestModerateActions);
|
||||
DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
|
||||
|
||||
void setDeleteConfirmedCallback(base::lambda<void()> callback) {
|
||||
void setDeleteConfirmedCallback(Fn<void()> callback) {
|
||||
_deleteConfirmedCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -192,7 +199,7 @@ private:
|
||||
object_ptr<Ui::Checkbox> _reportSpam = { nullptr };
|
||||
object_ptr<Ui::Checkbox> _deleteAll = { nullptr };
|
||||
|
||||
base::lambda<void()> _deleteConfirmedCallback;
|
||||
Fn<void()> _deleteConfirmedCallback;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -76,15 +76,14 @@ void SentCodeField::fix() {
|
||||
}
|
||||
}
|
||||
|
||||
SentCodeCall::SentCodeCall(QObject *parent, base::lambda_once<void()> callCallback, base::lambda<void()> updateCallback)
|
||||
: _timer(parent)
|
||||
, _call(std::move(callCallback))
|
||||
SentCodeCall::SentCodeCall(FnMut<void()> callCallback, Fn<void()> updateCallback)
|
||||
: _call(std::move(callCallback))
|
||||
, _update(std::move(updateCallback)) {
|
||||
_timer->connect(_timer, &QTimer::timeout, [this] {
|
||||
_timer.setCallback([=] {
|
||||
if (_status.state == State::Waiting) {
|
||||
if (--_status.timeout <= 0) {
|
||||
_status.state = State::Calling;
|
||||
_timer->stop();
|
||||
_timer.cancel();
|
||||
if (_call) {
|
||||
_call();
|
||||
}
|
||||
@@ -99,7 +98,7 @@ SentCodeCall::SentCodeCall(QObject *parent, base::lambda_once<void()> callCallba
|
||||
void SentCodeCall::setStatus(const Status &status) {
|
||||
_status = status;
|
||||
if (_status.state == State::Waiting) {
|
||||
_timer->start(1000);
|
||||
_timer.callEach(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +129,7 @@ void ConfirmPhoneBox::start(const QString &phone, const QString &hash) {
|
||||
ConfirmPhoneBox::ConfirmPhoneBox(QWidget*, const QString &phone, const QString &hash)
|
||||
: _phone(phone)
|
||||
, _hash(hash)
|
||||
, _call(this, [this] { sendCall(); }, [this] { update(); }) {
|
||||
, _call([this] { sendCall(); }, [this] { update(); }) {
|
||||
}
|
||||
|
||||
void ConfirmPhoneBox::sendCall() {
|
||||
@@ -198,17 +197,17 @@ void ConfirmPhoneBox::prepare() {
|
||||
_about->setMarkedText(aboutText);
|
||||
|
||||
_code.create(this, st::confirmPhoneCodeField, langFactory(lng_code_ph));
|
||||
_code->setAutoSubmit(_sentCodeLength, [this] { onSendCode(); });
|
||||
_code->setChangedCallback([this] { showError(QString()); });
|
||||
_code->setAutoSubmit(_sentCodeLength, [=] { sendCode(); });
|
||||
_code->setChangedCallback([=] { showError(QString()); });
|
||||
|
||||
setTitle(langFactory(lng_confirm_phone_title));
|
||||
|
||||
addButton(langFactory(lng_confirm_phone_send), [this] { onSendCode(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_confirm_phone_send), [=] { sendCode(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
setDimensions(st::boxWidth, st::usernamePadding.top() + _code->height() + st::usernameSkip + _about->height() + st::usernameSkip);
|
||||
|
||||
connect(_code, SIGNAL(submitted(bool)), this, SLOT(onSendCode()));
|
||||
connect(_code, &Ui::InputField::submitted, [=] { sendCode(); });
|
||||
|
||||
showChildren();
|
||||
}
|
||||
@@ -217,7 +216,7 @@ void ConfirmPhoneBox::callDone(const MTPauth_SentCode &result) {
|
||||
_call.callDone();
|
||||
}
|
||||
|
||||
void ConfirmPhoneBox::onSendCode() {
|
||||
void ConfirmPhoneBox::sendCode() {
|
||||
if (_sendCodeRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/timer.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
|
||||
namespace Ui {
|
||||
@@ -17,15 +18,15 @@ class FlatLabel;
|
||||
|
||||
class SentCodeField : public Ui::InputField {
|
||||
public:
|
||||
SentCodeField(QWidget *parent, const style::InputField &st, base::lambda<QString()> placeholderFactory = base::lambda<QString()>(), const QString &val = QString()) : Ui::InputField(parent, st, std::move(placeholderFactory), val) {
|
||||
SentCodeField(QWidget *parent, const style::InputField &st, Fn<QString()> placeholderFactory = Fn<QString()>(), const QString &val = QString()) : Ui::InputField(parent, st, std::move(placeholderFactory), val) {
|
||||
connect(this, &Ui::InputField::changed, [this] { fix(); });
|
||||
}
|
||||
|
||||
void setAutoSubmit(int length, base::lambda<void()> submitCallback) {
|
||||
void setAutoSubmit(int length, Fn<void()> submitCallback) {
|
||||
_autoSubmitLength = length;
|
||||
_submitCallback = std::move(submitCallback);
|
||||
}
|
||||
void setChangedCallback(base::lambda<void()> changedCallback) {
|
||||
void setChangedCallback(Fn<void()> changedCallback) {
|
||||
_changedCallback = std::move(changedCallback);
|
||||
}
|
||||
|
||||
@@ -36,14 +37,16 @@ private:
|
||||
bool _fixing = false;
|
||||
|
||||
int _autoSubmitLength = 0;
|
||||
base::lambda<void()> _submitCallback;
|
||||
base::lambda<void()> _changedCallback;
|
||||
Fn<void()> _submitCallback;
|
||||
Fn<void()> _changedCallback;
|
||||
|
||||
};
|
||||
|
||||
class SentCodeCall {
|
||||
public:
|
||||
SentCodeCall(QObject *parent, base::lambda_once<void()> callCallback, base::lambda<void()> updateCallback);
|
||||
SentCodeCall(
|
||||
FnMut<void()> callCallback,
|
||||
Fn<void()> updateCallback);
|
||||
|
||||
enum class State {
|
||||
Waiting,
|
||||
@@ -75,23 +78,18 @@ public:
|
||||
|
||||
private:
|
||||
Status _status;
|
||||
object_ptr<QTimer> _timer;
|
||||
base::lambda_once<void()> _call;
|
||||
base::lambda<void()> _update;
|
||||
base::Timer _timer;
|
||||
FnMut<void()> _call;
|
||||
Fn<void()> _update;
|
||||
|
||||
};
|
||||
|
||||
class ConfirmPhoneBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void start(const QString &phone, const QString &hash);
|
||||
|
||||
~ConfirmPhoneBox();
|
||||
|
||||
private slots:
|
||||
void onSendCode();
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
@@ -103,6 +101,7 @@ private:
|
||||
ConfirmPhoneBox(QWidget*, const QString &phone, const QString &hash);
|
||||
friend class object_ptr<ConfirmPhoneBox>;
|
||||
|
||||
void sendCode();
|
||||
void sendCall();
|
||||
void checkPhoneAndHash();
|
||||
|
||||
|
||||
@@ -125,8 +125,8 @@ public:
|
||||
ProxyBox(
|
||||
QWidget*,
|
||||
const ProxyData &data,
|
||||
base::lambda<void(ProxyData)> callback,
|
||||
base::lambda<void(ProxyData)> shareCallback);
|
||||
Fn<void(ProxyData)> callback,
|
||||
Fn<void(ProxyData)> shareCallback);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -148,13 +148,14 @@ private:
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
const QString &text) const;
|
||||
|
||||
base::lambda<void(ProxyData)> _callback;
|
||||
base::lambda<void(ProxyData)> _shareCallback;
|
||||
Fn<void(ProxyData)> _callback;
|
||||
Fn<void(ProxyData)> _shareCallback;
|
||||
|
||||
object_ptr<Ui::VerticalLayout> _content;
|
||||
|
||||
std::shared_ptr<Ui::RadioenumGroup<Type>> _type;
|
||||
|
||||
QPointer<Ui::SlideWrap<>> _aboutSponsored;
|
||||
QPointer<Ui::InputField> _host;
|
||||
QPointer<Ui::PortInput> _port;
|
||||
QPointer<Ui::InputField> _user;
|
||||
@@ -414,7 +415,7 @@ void ProxyRow::showMenu() {
|
||||
_menuToggle->installEventFilter(_menu);
|
||||
const auto addAction = [&](
|
||||
const QString &text,
|
||||
base::lambda<void()> callback) {
|
||||
Fn<void()> callback) {
|
||||
return _menu->addAction(text, std::move(callback));
|
||||
};
|
||||
addAction(lang(lng_proxy_menu_edit), [=] {
|
||||
@@ -591,7 +592,7 @@ int ProxiesBox::rowHeight() const {
|
||||
}
|
||||
|
||||
void ProxiesBox::addNewProxy() {
|
||||
Ui::show(_controller->addNewItemBox(), LayerOption::KeepOther);
|
||||
getDelegate()->show(_controller->addNewItemBox());
|
||||
}
|
||||
|
||||
void ProxiesBox::applyView(View &&view) {
|
||||
@@ -659,7 +660,7 @@ void ProxiesBox::setupButtons(int id, not_null<ProxyRow*> button) {
|
||||
|
||||
button->editClicks(
|
||||
) | rpl::start_with_next([=] {
|
||||
Ui::show(_controller->editItemBox(id), LayerOption::KeepOther);
|
||||
getDelegate()->show(_controller->editItemBox(id));
|
||||
}, button->lifetime());
|
||||
|
||||
button->shareClicks(
|
||||
@@ -676,8 +677,8 @@ void ProxiesBox::setupButtons(int id, not_null<ProxyRow*> button) {
|
||||
ProxyBox::ProxyBox(
|
||||
QWidget*,
|
||||
const ProxyData &data,
|
||||
base::lambda<void(ProxyData)> callback,
|
||||
base::lambda<void(ProxyData)> shareCallback)
|
||||
Fn<void(ProxyData)> callback,
|
||||
Fn<void(ProxyData)> shareCallback)
|
||||
: _callback(std::move(callback))
|
||||
, _shareCallback(std::move(shareCallback))
|
||||
, _content(this) {
|
||||
@@ -763,6 +764,16 @@ void ProxyBox::setupTypes() {
|
||||
label),
|
||||
st::proxyEditTypePadding);
|
||||
}
|
||||
_aboutSponsored = _content->add(object_ptr<Ui::SlideWrap<>>(
|
||||
_content,
|
||||
object_ptr<Ui::PaddingWrap<>>(
|
||||
_content,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_content,
|
||||
lang(lng_proxy_sponsor_warning),
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::boxDividerLabel),
|
||||
st::proxyAboutSponsorPadding)));
|
||||
}
|
||||
|
||||
void ProxyBox::setupSocketAddress(const ProxyData &data) {
|
||||
@@ -875,6 +886,9 @@ void ProxyBox::setupControls(const ProxyData &data) {
|
||||
_mtprotoCredentials->toggle(
|
||||
type == Type::Mtproto,
|
||||
anim::type::instant);
|
||||
_aboutSponsored->toggle(
|
||||
type == Type::Mtproto,
|
||||
anim::type::instant);
|
||||
};
|
||||
_type->setChangedCallback([=](Type type) {
|
||||
handleType(type);
|
||||
@@ -897,240 +911,6 @@ void ProxyBox::addLabel(
|
||||
|
||||
} // namespace
|
||||
|
||||
void ConnectionBox::ShowApplyProxyConfirmation(
|
||||
Type type,
|
||||
const QMap<QString, QString> &fields) {
|
||||
const auto server = fields.value(qsl("server"));
|
||||
const auto port = fields.value(qsl("port")).toUInt();
|
||||
auto proxy = ProxyData();
|
||||
proxy.type = type;
|
||||
proxy.host = server;
|
||||
proxy.port = port;
|
||||
if (type == Type::Socks5) {
|
||||
proxy.user = fields.value(qsl("user"));
|
||||
proxy.password = fields.value(qsl("pass"));
|
||||
} else if (type == Type::Mtproto) {
|
||||
proxy.password = fields.value(qsl("secret"));
|
||||
}
|
||||
if (proxy) {
|
||||
const auto box = std::make_shared<QPointer<ConfirmBox>>();
|
||||
const auto text = lng_sure_enable_socks(
|
||||
lt_server,
|
||||
server,
|
||||
lt_port,
|
||||
QString::number(port));
|
||||
*box = Ui::show(Box<ConfirmBox>(text, lang(lng_sure_enable), [=] {
|
||||
auto &proxies = Global::RefProxiesList();
|
||||
if (ranges::find(proxies, proxy) == end(proxies)) {
|
||||
proxies.push_back(proxy);
|
||||
}
|
||||
Global::SetSelectedProxy(proxy);
|
||||
Global::SetUseProxy(true);
|
||||
Local::writeSettings();
|
||||
Sandbox::refreshGlobalProxy();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
MTP::restart();
|
||||
if (const auto strong = box->data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}), LayerOption::KeepOther);
|
||||
}
|
||||
}
|
||||
|
||||
ConnectionBox::ConnectionBox(QWidget *parent)
|
||||
: _hostInput(this, st::connectionHostInputField, langFactory(lng_connection_host_ph), Global::SelectedProxy().host)
|
||||
, _portInput(this, st::connectionPortInputField, langFactory(lng_connection_port_ph), QString::number(Global::SelectedProxy().port))
|
||||
, _userInput(this, st::connectionUserInputField, langFactory(lng_connection_user_ph), Global::SelectedProxy().user)
|
||||
, _passwordInput(this, st::connectionPasswordInputField, langFactory(lng_connection_password_ph), Global::SelectedProxy().password)
|
||||
, _typeGroup(std::make_shared<Ui::RadioenumGroup<Type>>(Global::SelectedProxy().type))
|
||||
, _autoRadio(this, _typeGroup, Type::None, lang(lng_connection_auto_rb), st::defaultBoxCheckbox)
|
||||
, _httpProxyRadio(this, _typeGroup, Type::Http, lang(lng_connection_http_proxy_rb), st::defaultBoxCheckbox)
|
||||
, _tcpProxyRadio(this, _typeGroup, Type::Socks5, lang(lng_connection_tcp_proxy_rb), st::defaultBoxCheckbox)
|
||||
, _tryIPv6(this, lang(lng_connection_try_ipv6), Global::TryIPv6(), st::defaultBoxCheckbox) {
|
||||
}
|
||||
|
||||
void ConnectionBox::prepare() {
|
||||
setTitle(langFactory(lng_connection_header));
|
||||
|
||||
addButton(langFactory(lng_connection_save), [this] { onSave(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
|
||||
_typeGroup->setChangedCallback([this](Type value) { typeChanged(value); });
|
||||
|
||||
connect(_hostInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_portInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_userInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_passwordInput, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_hostInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
|
||||
connect(_portInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
|
||||
connect(_userInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
|
||||
connect(_passwordInput, SIGNAL(focused()), this, SLOT(onFieldFocus()));
|
||||
|
||||
updateControlsVisibility();
|
||||
}
|
||||
|
||||
bool ConnectionBox::badProxyValue() const {
|
||||
return (_hostInput->getLastText().isEmpty() || !_portInput->getLastText().toInt());
|
||||
}
|
||||
|
||||
void ConnectionBox::updateControlsVisibility() {
|
||||
auto newHeight = st::boxOptionListPadding.top() + _autoRadio->heightNoMargins() + st::boxOptionListSkip + _httpProxyRadio->heightNoMargins() + st::boxOptionListSkip + _tcpProxyRadio->heightNoMargins() + st::boxOptionListSkip + st::connectionIPv6Skip + _tryIPv6->heightNoMargins() + st::defaultCheckbox.margin.bottom() + st::boxOptionListPadding.bottom() + st::boxPadding.bottom();
|
||||
if (!proxyFieldsVisible()) {
|
||||
_hostInput->hide();
|
||||
_portInput->hide();
|
||||
_userInput->hide();
|
||||
_passwordInput->hide();
|
||||
} else {
|
||||
newHeight += 2 * st::boxOptionInputSkip + 2 * _hostInput->height();
|
||||
_hostInput->show();
|
||||
_portInput->show();
|
||||
_userInput->show();
|
||||
_passwordInput->show();
|
||||
}
|
||||
|
||||
setDimensions(st::boxWidth, newHeight);
|
||||
updateControlsPosition();
|
||||
}
|
||||
|
||||
bool ConnectionBox::proxyFieldsVisible() const {
|
||||
return (_typeGroup->value() == Type::Http
|
||||
|| _typeGroup->value() == Type::Socks5);
|
||||
}
|
||||
|
||||
void ConnectionBox::setInnerFocus() {
|
||||
if (proxyFieldsVisible()) {
|
||||
_hostInput->setFocusFast();
|
||||
} else {
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionBox::resizeEvent(QResizeEvent *e) {
|
||||
BoxContent::resizeEvent(e);
|
||||
|
||||
updateControlsPosition();
|
||||
}
|
||||
|
||||
void ConnectionBox::updateControlsPosition() {
|
||||
auto type = _typeGroup->value();
|
||||
_autoRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->getMargins().top() + st::boxOptionListPadding.top());
|
||||
_httpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _autoRadio->bottomNoMargins() + st::boxOptionListSkip);
|
||||
|
||||
auto inputy = 0;
|
||||
auto fieldsVisible = proxyFieldsVisible();
|
||||
auto fieldsBelowHttp = fieldsVisible && (type == Type::Http);
|
||||
auto fieldsBelowTcp = fieldsVisible && (type == Type::Socks5);
|
||||
if (fieldsBelowHttp) {
|
||||
inputy = _httpProxyRadio->bottomNoMargins() + st::boxOptionInputSkip;
|
||||
_tcpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), inputy + st::boxOptionInputSkip + 2 * _hostInput->height() + st::boxOptionListSkip);
|
||||
} else {
|
||||
_tcpProxyRadio->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _httpProxyRadio->bottomNoMargins() + st::boxOptionListSkip);
|
||||
if (fieldsBelowTcp) {
|
||||
inputy = _tcpProxyRadio->bottomNoMargins() + st::boxOptionInputSkip;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputy) {
|
||||
_hostInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultCheck.diameter + st::defaultBoxCheckbox.textPosition.x() - st::defaultInputField.textMargins.left(), inputy);
|
||||
_portInput->moveToRight(st::boxPadding.right(), inputy);
|
||||
_userInput->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() + st::defaultCheck.diameter + st::defaultBoxCheckbox.textPosition.x() - st::defaultInputField.textMargins.left(), _hostInput->y() + _hostInput->height() + st::boxOptionInputSkip);
|
||||
_passwordInput->moveToRight(st::boxPadding.right(), _userInput->y());
|
||||
}
|
||||
|
||||
auto tryipv6y = (fieldsBelowTcp ? _userInput->bottomNoMargins() : _tcpProxyRadio->bottomNoMargins()) + st::boxOptionListSkip + st::connectionIPv6Skip;
|
||||
_tryIPv6->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), tryipv6y);
|
||||
}
|
||||
|
||||
void ConnectionBox::typeChanged(Type type) {
|
||||
if (!proxyFieldsVisible()) {
|
||||
setFocus();
|
||||
}
|
||||
updateControlsVisibility();
|
||||
if (proxyFieldsVisible()) {
|
||||
if (!_hostInput->hasFocus() && !_portInput->hasFocus() && !_userInput->hasFocus() && !_passwordInput->hasFocus()) {
|
||||
_hostInput->setFocusFast();
|
||||
}
|
||||
if ((type == Type::Http) && !_portInput->getLastText().toInt()) {
|
||||
_portInput->setText(qsl("80"));
|
||||
_portInput->finishAnimating();
|
||||
}
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void ConnectionBox::onFieldFocus() {
|
||||
}
|
||||
|
||||
void ConnectionBox::onSubmit() {
|
||||
onFieldFocus();
|
||||
if (_hostInput->hasFocus()) {
|
||||
if (!_hostInput->getLastText().trimmed().isEmpty()) {
|
||||
_portInput->setFocus();
|
||||
} else {
|
||||
_hostInput->showError();
|
||||
}
|
||||
} else if (_portInput->hasFocus()) {
|
||||
if (_portInput->getLastText().trimmed().toInt() > 0) {
|
||||
_userInput->setFocus();
|
||||
} else {
|
||||
_portInput->showError();
|
||||
}
|
||||
} else if (_userInput->hasFocus()) {
|
||||
_passwordInput->setFocus();
|
||||
} else if (_passwordInput->hasFocus()) {
|
||||
if (_hostInput->getLastText().trimmed().isEmpty()) {
|
||||
_hostInput->setFocus();
|
||||
_hostInput->showError();
|
||||
} else if (_portInput->getLastText().trimmed().toInt() <= 0) {
|
||||
_portInput->setFocus();
|
||||
_portInput->showError();
|
||||
} else {
|
||||
onSave();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionBox::onSave() {
|
||||
auto proxy = ProxyData();
|
||||
proxy.host = _hostInput->getLastText().trimmed();
|
||||
proxy.user = _userInput->getLastText().trimmed();
|
||||
proxy.password = _passwordInput->getLastText().trimmed();
|
||||
proxy.port = _portInput->getLastText().toUInt();
|
||||
|
||||
auto type = _typeGroup->value();
|
||||
if (type == Type::None) {
|
||||
proxy = ProxyData();
|
||||
} else if (type == Type::Mtproto) {
|
||||
proxy = Global::SelectedProxy();
|
||||
} else {
|
||||
if (proxy.host.isEmpty()) {
|
||||
_hostInput->showError();
|
||||
return;
|
||||
} else if (!proxy.port) {
|
||||
_portInput->showError();
|
||||
return;
|
||||
}
|
||||
proxy.type = type;
|
||||
}
|
||||
Global::SetSelectedProxy(proxy ? proxy : ProxyData());
|
||||
Global::SetUseProxy(proxy ? true : false);
|
||||
if (cPlatform() == dbipWindows && Global::TryIPv6() != _tryIPv6->checked()) {
|
||||
Global::SetTryIPv6(_tryIPv6->checked());
|
||||
Local::writeSettings();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
|
||||
App::restart();
|
||||
} else {
|
||||
Global::SetTryIPv6(_tryIPv6->checked());
|
||||
Local::writeSettings();
|
||||
Sandbox::refreshGlobalProxy();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
|
||||
MTP::restart();
|
||||
closeBox();
|
||||
}
|
||||
}
|
||||
|
||||
AutoDownloadBox::AutoDownloadBox(QWidget *parent)
|
||||
: _photoPrivate(this, lang(lng_media_auto_private_chats), !(cAutoDownloadPhoto() & dbiadNoPrivate), st::defaultBoxCheckbox)
|
||||
, _photoGroups(this, lang(lng_media_auto_groups), !(cAutoDownloadPhoto() & dbiadNoGroups), st::defaultBoxCheckbox)
|
||||
@@ -1259,6 +1039,45 @@ ProxiesBoxController::ProxiesBoxController()
|
||||
}
|
||||
}
|
||||
|
||||
void ProxiesBoxController::ShowApplyConfirmation(
|
||||
Type type,
|
||||
const QMap<QString, QString> &fields) {
|
||||
const auto server = fields.value(qsl("server"));
|
||||
const auto port = fields.value(qsl("port")).toUInt();
|
||||
auto proxy = ProxyData();
|
||||
proxy.type = type;
|
||||
proxy.host = server;
|
||||
proxy.port = port;
|
||||
if (type == Type::Socks5) {
|
||||
proxy.user = fields.value(qsl("user"));
|
||||
proxy.password = fields.value(qsl("pass"));
|
||||
} else if (type == Type::Mtproto) {
|
||||
proxy.password = fields.value(qsl("secret"));
|
||||
}
|
||||
if (proxy) {
|
||||
const auto box = std::make_shared<QPointer<ConfirmBox>>();
|
||||
const auto text = lng_sure_enable_socks(
|
||||
lt_server,
|
||||
server,
|
||||
lt_port,
|
||||
QString::number(port))
|
||||
+ (proxy.type == Type::Mtproto
|
||||
? "\n\n" + lang(lng_proxy_sponsor_warning)
|
||||
: QString());
|
||||
*box = Ui::show(Box<ConfirmBox>(text, lang(lng_sure_enable), [=] {
|
||||
auto &proxies = Global::RefProxiesList();
|
||||
if (ranges::find(proxies, proxy) == end(proxies)) {
|
||||
proxies.push_back(proxy);
|
||||
}
|
||||
Messenger::Instance().setCurrentProxy(proxy, true);
|
||||
Local::writeSettings();
|
||||
if (const auto strong = box->data()) {
|
||||
strong->closeBox();
|
||||
}
|
||||
}), LayerOption::KeepOther);
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<bool> ProxiesBoxController::proxyEnabledValue() const {
|
||||
return _proxyEnabledChanges.events_starting_with_copy(
|
||||
Global::UseProxy()
|
||||
@@ -1275,15 +1094,16 @@ void ProxiesBoxController::refreshChecker(Item &item) {
|
||||
|
||||
item.state = ItemState::Checking;
|
||||
const auto setup = [&](Checker &checker) {
|
||||
checker = MTP::internal::AbstractConnection::create(
|
||||
checker = MTP::internal::AbstractConnection::Create(
|
||||
mtproto,
|
||||
type,
|
||||
QThread::currentThread());
|
||||
QThread::currentThread(),
|
||||
item.data);
|
||||
setupChecker(item.id, checker);
|
||||
};
|
||||
setup(item.checker);
|
||||
if (item.data.type == Type::Mtproto) {
|
||||
item.checkerv6 = nullptr;
|
||||
item.checker->setProxyOverride(item.data);
|
||||
item.checker->connectToServer(
|
||||
item.data.host,
|
||||
item.data.port,
|
||||
@@ -1312,7 +1132,6 @@ void ProxiesBoxController::refreshChecker(Item &item) {
|
||||
const Checker &checker,
|
||||
const std::vector<MTP::DcOptions::Endpoint> &endpoints) {
|
||||
if (checker) {
|
||||
checker->setProxyOverride(item.data);
|
||||
checker->connectToServer(
|
||||
QString::fromStdString(endpoints.front().ip),
|
||||
endpoints.front().port,
|
||||
@@ -1366,7 +1185,7 @@ object_ptr<BoxContent> ProxiesBoxController::CreateOwningBox() {
|
||||
|
||||
object_ptr<BoxContent> ProxiesBoxController::create() {
|
||||
auto result = Box<ProxiesBox>(this);
|
||||
for (const auto &item : base::reversed(_list)) {
|
||||
for (const auto &item : _list) {
|
||||
updateView(item);
|
||||
}
|
||||
return std::move(result);
|
||||
@@ -1411,9 +1230,8 @@ void ProxiesBoxController::applyItem(int id) {
|
||||
|
||||
auto j = findByProxy(Global::SelectedProxy());
|
||||
|
||||
Global::SetSelectedProxy(item->data);
|
||||
Global::SetUseProxy(true);
|
||||
applyChanges();
|
||||
Messenger::Instance().setCurrentProxy(item->data, true);
|
||||
saveDelayed();
|
||||
|
||||
if (j != end(_list)) {
|
||||
updateView(*j);
|
||||
@@ -1433,8 +1251,10 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) {
|
||||
_lastSelectedProxy = base::take(Global::RefSelectedProxy());
|
||||
if (Global::UseProxy()) {
|
||||
_lastSelectedProxyUsed = true;
|
||||
Global::SetUseProxy(false);
|
||||
applyChanges();
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
ProxyData(),
|
||||
false);
|
||||
saveDelayed();
|
||||
} else {
|
||||
_lastSelectedProxyUsed = false;
|
||||
}
|
||||
@@ -1455,10 +1275,12 @@ void ProxiesBoxController::setDeleted(int id, bool deleted) {
|
||||
if (!Global::SelectedProxy() && _lastSelectedProxy == item->data) {
|
||||
Assert(!Global::UseProxy());
|
||||
|
||||
Global::SetSelectedProxy(base::take(_lastSelectedProxy));
|
||||
if (base::take(_lastSelectedProxyUsed)) {
|
||||
Global::SetUseProxy(true);
|
||||
applyChanges();
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
base::take(_lastSelectedProxy),
|
||||
true);
|
||||
} else {
|
||||
Global::SetSelectedProxy(base::take(_lastSelectedProxy));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1559,8 +1381,10 @@ bool ProxiesBoxController::setProxyEnabled(bool enabled) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Global::SetUseProxy(enabled);
|
||||
applyChanges();
|
||||
Messenger::Instance().setCurrentProxy(
|
||||
Global::SelectedProxy(),
|
||||
enabled);
|
||||
saveDelayed();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1580,13 +1404,8 @@ void ProxiesBoxController::setTryIPv6(bool enabled) {
|
||||
return;
|
||||
}
|
||||
Global::SetTryIPv6(enabled);
|
||||
applyChanges();
|
||||
}
|
||||
|
||||
void ProxiesBoxController::applyChanges() {
|
||||
Sandbox::refreshGlobalProxy();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
MTP::restart();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
saveDelayed();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,48 +22,6 @@ template <typename Enum>
|
||||
class Radioenum;
|
||||
} // namespace Ui
|
||||
|
||||
class ConnectionBox : public BoxContent {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Type = ProxyData::Type;
|
||||
|
||||
ConnectionBox(QWidget *parent);
|
||||
|
||||
static void ShowApplyProxyConfirmation(
|
||||
Type type,
|
||||
const QMap<QString, QString> &fields);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void onSubmit();
|
||||
void onFieldFocus();
|
||||
void onSave();
|
||||
|
||||
private:
|
||||
void typeChanged(Type type);
|
||||
void updateControlsVisibility();
|
||||
void updateControlsPosition();
|
||||
bool badProxyValue() const;
|
||||
bool proxyFieldsVisible() const;
|
||||
|
||||
object_ptr<Ui::InputField> _hostInput;
|
||||
object_ptr<Ui::PortInput> _portInput;
|
||||
object_ptr<Ui::InputField> _userInput;
|
||||
object_ptr<Ui::PasswordInput> _passwordInput;
|
||||
std::shared_ptr<Ui::RadioenumGroup<Type>> _typeGroup;
|
||||
object_ptr<Ui::Radioenum<Type>> _autoRadio;
|
||||
object_ptr<Ui::Radioenum<Type>> _httpProxyRadio;
|
||||
object_ptr<Ui::Radioenum<Type>> _tcpProxyRadio;
|
||||
object_ptr<Ui::Checkbox> _tryIPv6;
|
||||
|
||||
};
|
||||
|
||||
class AutoDownloadBox : public BoxContent {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -98,6 +56,10 @@ public:
|
||||
|
||||
ProxiesBoxController();
|
||||
|
||||
static void ShowApplyConfirmation(
|
||||
Type type,
|
||||
const QMap<QString, QString> &fields);
|
||||
|
||||
static object_ptr<BoxContent> CreateOwningBox();
|
||||
object_ptr<BoxContent> create();
|
||||
|
||||
@@ -155,7 +117,6 @@ private:
|
||||
void setDeleted(int id, bool deleted);
|
||||
void updateView(const Item &item);
|
||||
void share(const ProxyData &proxy);
|
||||
void applyChanges();
|
||||
void saveDelayed();
|
||||
void refreshChecker(Item &item);
|
||||
void setupChecker(int id, const Checker &checker);
|
||||
|
||||
@@ -83,20 +83,25 @@ void DownloadPathBox::radioChanged(Directory value) {
|
||||
}
|
||||
|
||||
void DownloadPathBox::onEditPath() {
|
||||
auto initialPath = [] {
|
||||
const auto initialPath = [] {
|
||||
if (!Global::DownloadPath().isEmpty() && Global::DownloadPath() != qstr("tmp")) {
|
||||
return Global::DownloadPath().left(Global::DownloadPath().size() - (Global::DownloadPath().endsWith('/') ? 1 : 0));
|
||||
}
|
||||
return QString();
|
||||
};
|
||||
FileDialog::GetFolder(lang(lng_download_path_choose), initialPath(), base::lambda_guarded(this, [this](const QString &result) {
|
||||
}();
|
||||
const auto handleFolder = [=](const QString &result) {
|
||||
if (!result.isEmpty()) {
|
||||
_path = result + '/';
|
||||
_pathBookmark = psDownloadPathBookmark(_path);
|
||||
setPathText(QDir::toNativeSeparators(_path));
|
||||
_group->setValue(Directory::Custom);
|
||||
}
|
||||
}));
|
||||
};
|
||||
FileDialog::GetFolder(
|
||||
this,
|
||||
lang(lng_download_path_choose),
|
||||
initialPath,
|
||||
crl::guard(this, handleFolder));
|
||||
}
|
||||
|
||||
void DownloadPathBox::save() {
|
||||
|
||||
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_document.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "mainwidget.h"
|
||||
#include "layout.h"
|
||||
@@ -24,8 +25,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
EditCaptionBox::EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<HistoryItem*> item)
|
||||
: _msgId(item->fullId()) {
|
||||
: _controller(controller)
|
||||
, _msgId(item->fullId()) {
|
||||
Expects(item->media() != nullptr);
|
||||
Expects(item->media()->allowsEditCaption());
|
||||
|
||||
@@ -50,7 +53,11 @@ EditCaptionBox::EditCaptionBox(
|
||||
}
|
||||
doc = document;
|
||||
}
|
||||
auto caption = item->originalText().text;
|
||||
const auto original = item->originalText();
|
||||
const auto editData = TextWithTags {
|
||||
original.text,
|
||||
ConvertEntitiesToTextTags(original.entities)
|
||||
};
|
||||
|
||||
if (!_animated && (dimensions.isEmpty() || doc || image->isNull())) {
|
||||
if (image->isNull()) {
|
||||
@@ -130,9 +137,19 @@ EditCaptionBox::EditCaptionBox(
|
||||
}
|
||||
Assert(_animated || _photo || _doc);
|
||||
|
||||
_field.create(this, st::confirmCaptionArea, langFactory(lng_photo_caption), caption);
|
||||
_field.create(
|
||||
this,
|
||||
st::confirmCaptionArea,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_photo_caption),
|
||||
editData);
|
||||
_field->setMaxLength(MaxPhotoCaption);
|
||||
_field->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_field->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
_field->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_field->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
_field->setMarkdownReplacesEnabled(rpl::single(true));
|
||||
_field->setEditLinkCallback(
|
||||
DefaultEditLinkCallback(_controller, _field));
|
||||
}
|
||||
|
||||
void EditCaptionBox::prepareGifPreview(DocumentData *document) {
|
||||
@@ -177,13 +194,9 @@ void EditCaptionBox::prepare() {
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
|
||||
updateBoxSize();
|
||||
connect(_field, &Ui::InputArea::submitted, this, [this] { save(); });
|
||||
connect(_field, &Ui::InputArea::cancelled, this, [this] {
|
||||
closeBox();
|
||||
});
|
||||
connect(_field, &Ui::InputArea::resized, this, [this] {
|
||||
captionResized();
|
||||
});
|
||||
connect(_field, &Ui::InputField::submitted, [=] { save(); });
|
||||
connect(_field, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
connect(_field, &Ui::InputField::resized, [=] { captionResized(); });
|
||||
|
||||
auto cursor = _field->textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
@@ -228,7 +241,7 @@ void EditCaptionBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
if (_gifPreview && _gifPreview->started()) {
|
||||
auto s = QSize(_thumbw, _thumbh);
|
||||
auto paused = controller()->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
|
||||
auto paused = _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Layer);
|
||||
auto frame = _gifPreview->current(s.width(), s.height(), s.width(), s.height(), ImageRoundRadius::None, RectPart::None, paused ? 0 : getms());
|
||||
p.drawPixmap(_thumbx, st::boxPhotoPadding.top(), frame);
|
||||
} else {
|
||||
@@ -332,17 +345,30 @@ void EditCaptionBox::save() {
|
||||
if (_previewCancelled) {
|
||||
flags |= MTPmessages_EditMessage::Flag::f_no_webpage;
|
||||
}
|
||||
MTPVector<MTPMessageEntity> sentEntities;
|
||||
const auto textWithTags = _field->getTextWithAppliedMarkdown();
|
||||
auto sending = TextWithEntities{
|
||||
textWithTags.text,
|
||||
ConvertTextTagsToEntities(textWithTags.tags)
|
||||
};
|
||||
const auto prepareFlags = Ui::ItemTextOptions(
|
||||
item->history(),
|
||||
App::self()).flags;
|
||||
TextUtilities::PrepareForSending(sending, prepareFlags);
|
||||
TextUtilities::Trim(sending);
|
||||
|
||||
const auto sentEntities = TextUtilities::EntitiesToMTP(
|
||||
sending.entities,
|
||||
TextUtilities::ConvertOption::SkipLocal);
|
||||
if (!sentEntities.v.isEmpty()) {
|
||||
flags |= MTPmessages_EditMessage::Flag::f_entities;
|
||||
}
|
||||
auto text = TextUtilities::PrepareForSending(_field->getLastText(), TextUtilities::PrepareTextOption::CheckLinks);
|
||||
_saveRequestId = MTP::send(
|
||||
MTPmessages_EditMessage(
|
||||
MTP_flags(flags),
|
||||
item->history()->peer->input,
|
||||
MTP_int(item->id),
|
||||
MTP_string(text),
|
||||
MTP_string(sending.text),
|
||||
MTPInputMedia(),
|
||||
MTPnullMarkup,
|
||||
sentEntities,
|
||||
MTP_inputGeoPointEmpty()),
|
||||
|
||||
@@ -9,17 +9,28 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
namespace Data {
|
||||
class Media;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
class InputArea;
|
||||
class InputField;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
class EditCaptionBox : public BoxContent, public RPCSender {
|
||||
public:
|
||||
EditCaptionBox(QWidget*, not_null<HistoryItem*> item);
|
||||
EditCaptionBox(
|
||||
QWidget*,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<HistoryItem*> item);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -41,6 +52,7 @@ private:
|
||||
|
||||
int errorTopSkip() const;
|
||||
|
||||
not_null<Window::Controller*> _controller;
|
||||
FullMsgId _msgId;
|
||||
bool _animated = false;
|
||||
bool _photo = false;
|
||||
@@ -49,7 +61,7 @@ private:
|
||||
QPixmap _thumb;
|
||||
Media::Clip::ReaderPointer _gifPreview;
|
||||
|
||||
object_ptr<Ui::InputArea> _field = { nullptr };
|
||||
object_ptr<Ui::InputField> _field = { nullptr };
|
||||
|
||||
int _thumbx = 0;
|
||||
int _thumbw = 0;
|
||||
|
||||
@@ -630,38 +630,45 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
|
||||
}
|
||||
|
||||
void EditColorBox::prepare() {
|
||||
setTitle([this] { return _title; });
|
||||
setTitle([=] { return _title; });
|
||||
|
||||
connect(_hueField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
connect(_saturationField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
connect(_brightnessField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
connect(_redField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
connect(_greenField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
connect(_blueField, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
connect(_result, SIGNAL(changed()), this, SLOT(onFieldChanged()));
|
||||
const auto hsvChanged = [=] { updateFromHSVFields(); };
|
||||
const auto rgbChanged = [=] { updateFromRGBFields(); };
|
||||
connect(_hueField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_saturationField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_brightnessField, &Ui::MaskedInputField::changed, hsvChanged);
|
||||
connect(_redField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_greenField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_blueField, &Ui::MaskedInputField::changed, rgbChanged);
|
||||
connect(_result, &Ui::MaskedInputField::changed, [=] {
|
||||
updateFromResultField();
|
||||
});
|
||||
|
||||
connect(_hueField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
connect(_saturationField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
connect(_brightnessField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
connect(_redField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
connect(_greenField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
connect(_blueField, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
connect(_result, SIGNAL(submitted(bool)), this, SLOT(onFieldSubmitted()));
|
||||
const auto submitted = [=] { fieldSubmitted(); };
|
||||
connect(_hueField, &Ui::MaskedInputField::submitted, submitted);
|
||||
connect(_saturationField, &Ui::MaskedInputField::submitted, submitted);
|
||||
connect(_brightnessField, &Ui::MaskedInputField::submitted, submitted);
|
||||
connect(_redField, &Ui::MaskedInputField::submitted, submitted);
|
||||
connect(_greenField, &Ui::MaskedInputField::submitted, submitted);
|
||||
connect(_blueField, &Ui::MaskedInputField::submitted, submitted);
|
||||
connect(_result, &Ui::MaskedInputField::submitted, submitted);
|
||||
|
||||
addButton(langFactory(lng_settings_save), [this] { saveColor(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_settings_save), [=] { saveColor(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
auto height = st::colorEditSkip + st::colorPickerSize + st::colorEditSkip + st::colorSliderWidth + st::colorEditSkip;
|
||||
setDimensions(st::colorEditWidth, height);
|
||||
|
||||
subscribe(_picker->changed(), [this] { updateFromControls(); });
|
||||
subscribe(_hueSlider->changed(), [this] { updateFromControls(); });
|
||||
subscribe(_opacitySlider->changed(), [this] { updateFromControls(); });
|
||||
subscribe(boxClosing, [this] {
|
||||
subscribe(_picker->changed(), [=] { updateFromControls(); });
|
||||
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
|
||||
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
|
||||
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (_cancelCallback) {
|
||||
_cancelCallback();
|
||||
}
|
||||
});
|
||||
}, lifetime());
|
||||
|
||||
updateFromControls();
|
||||
}
|
||||
|
||||
@@ -670,30 +677,7 @@ void EditColorBox::setInnerFocus() {
|
||||
_result->selectAll();
|
||||
}
|
||||
|
||||
void EditColorBox::onFieldChanged() {
|
||||
auto emitter = sender();
|
||||
auto checkHSVSender = [this, emitter](QObject *field) {
|
||||
if (emitter == field) {
|
||||
updateFromHSVFields();
|
||||
}
|
||||
};
|
||||
auto checkRGBSender = [this, emitter](QObject *field) {
|
||||
if (emitter == field) {
|
||||
updateFromRGBFields();
|
||||
}
|
||||
};
|
||||
checkHSVSender(_hueField);
|
||||
checkHSVSender(_saturationField);
|
||||
checkHSVSender(_brightnessField);
|
||||
checkRGBSender(_redField);
|
||||
checkRGBSender(_greenField);
|
||||
checkRGBSender(_blueField);
|
||||
if (emitter == _result) {
|
||||
updateFromResultField();
|
||||
}
|
||||
}
|
||||
|
||||
void EditColorBox::onFieldSubmitted() {
|
||||
void EditColorBox::fieldSubmitted() {
|
||||
Ui::MaskedInputField *fields[] = {
|
||||
_hueField,
|
||||
_saturationField,
|
||||
@@ -716,7 +700,7 @@ void EditColorBox::onFieldSubmitted() {
|
||||
}
|
||||
|
||||
void EditColorBox::saveColor() {
|
||||
_cancelCallback = base::lambda<void()>();
|
||||
_cancelCallback = Fn<void()>();
|
||||
if (_saveCallback) {
|
||||
_saveCallback(_new.toRgb());
|
||||
}
|
||||
|
||||
@@ -10,16 +10,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
class EditColorBox : public BoxContent {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EditColorBox(QWidget*, const QString &title, QColor current = QColor(255, 255, 255));
|
||||
|
||||
void setSaveCallback(base::lambda<void(QColor)> callback) {
|
||||
void setSaveCallback(Fn<void(QColor)> callback) {
|
||||
_saveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void setCancelCallback(base::lambda<void()> callback) {
|
||||
void setCancelCallback(Fn<void()> callback) {
|
||||
_cancelCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -37,12 +35,9 @@ protected:
|
||||
|
||||
void setInnerFocus() override;
|
||||
|
||||
private slots:
|
||||
void onFieldChanged();
|
||||
void onFieldSubmitted();
|
||||
|
||||
private:
|
||||
void saveColor();
|
||||
void fieldSubmitted();
|
||||
|
||||
void updateFromColor(QColor color);
|
||||
void updateControlsFromColor();
|
||||
@@ -90,7 +85,7 @@ private:
|
||||
QRect _currentRect;
|
||||
QRect _newRect;
|
||||
|
||||
base::lambda<void(QColor)> _saveCallback;
|
||||
base::lambda<void()> _cancelCallback;
|
||||
Fn<void(QColor)> _saveCallback;
|
||||
Fn<void()> _cancelCallback;
|
||||
|
||||
};
|
||||
|
||||
@@ -66,7 +66,6 @@ class EditParticipantBox::Inner : public TWidget {
|
||||
public:
|
||||
Inner(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user,
|
||||
bool hasAdminRights);
|
||||
@@ -101,7 +100,6 @@ private:
|
||||
|
||||
EditParticipantBox::Inner::Inner(
|
||||
QWidget *parent,
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user,
|
||||
bool hasAdminRights)
|
||||
@@ -110,7 +108,6 @@ EditParticipantBox::Inner::Inner(
|
||||
, _user(user)
|
||||
, _userPhoto(
|
||||
this,
|
||||
controller,
|
||||
_user,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
st::rightsPhotoButton)
|
||||
@@ -184,7 +181,6 @@ EditParticipantBox::EditParticipantBox(QWidget*, not_null<ChannelData*> channel,
|
||||
void EditParticipantBox::prepare() {
|
||||
_inner = setInnerWidget(object_ptr<Inner>(
|
||||
this,
|
||||
controller(),
|
||||
_channel,
|
||||
_user,
|
||||
hasAdminRights()));
|
||||
|
||||
@@ -58,7 +58,7 @@ class EditAdminBox : public EditParticipantBox {
|
||||
public:
|
||||
EditAdminBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, const MTPChannelAdminRights &rights);
|
||||
|
||||
void setSaveCallback(base::lambda<void(MTPChannelAdminRights, MTPChannelAdminRights)> callback) {
|
||||
void setSaveCallback(Fn<void(MTPChannelAdminRights, MTPChannelAdminRights)> callback) {
|
||||
_saveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ private:
|
||||
|
||||
const MTPChannelAdminRights _oldRights;
|
||||
std::vector<std::pair<Flag, Flag>> _dependencies;
|
||||
base::lambda<void(MTPChannelAdminRights, MTPChannelAdminRights)> _saveCallback;
|
||||
Fn<void(MTPChannelAdminRights, MTPChannelAdminRights)> _saveCallback;
|
||||
|
||||
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
|
||||
QPointer<Ui::FlatLabel> _aboutAddAdmins;
|
||||
@@ -93,7 +93,7 @@ class EditRestrictedBox : public EditParticipantBox {
|
||||
public:
|
||||
EditRestrictedBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights, const MTPChannelBannedRights &rights);
|
||||
|
||||
void setSaveCallback(base::lambda<void(MTPChannelBannedRights, MTPChannelBannedRights)> callback) {
|
||||
void setSaveCallback(Fn<void(MTPChannelBannedRights, MTPChannelBannedRights)> callback) {
|
||||
_saveCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ private:
|
||||
const MTPChannelBannedRights _oldRights;
|
||||
TimeId _until = 0;
|
||||
std::vector<std::pair<Flag, Flag>> _dependencies;
|
||||
base::lambda<void(MTPChannelBannedRights, MTPChannelBannedRights)> _saveCallback;
|
||||
Fn<void(MTPChannelBannedRights, MTPChannelBannedRights)> _saveCallback;
|
||||
|
||||
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace {
|
||||
|
||||
class PrivacyExceptionsBoxController : public ChatsListBoxController {
|
||||
public:
|
||||
PrivacyExceptionsBoxController(base::lambda<QString()> titleFactory, const std::vector<not_null<UserData*>> &selected);
|
||||
PrivacyExceptionsBoxController(Fn<QString()> titleFactory, const std::vector<not_null<UserData*>> &selected);
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
std::vector<not_null<UserData*>> getResult() const;
|
||||
@@ -32,12 +32,12 @@ protected:
|
||||
std::unique_ptr<Row> createRow(not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
base::lambda<QString()> _titleFactory;
|
||||
Fn<QString()> _titleFactory;
|
||||
std::vector<not_null<UserData*>> _selected;
|
||||
|
||||
};
|
||||
|
||||
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(base::lambda<QString()> titleFactory, const std::vector<not_null<UserData*>> &selected)
|
||||
PrivacyExceptionsBoxController::PrivacyExceptionsBoxController(Fn<QString()> titleFactory, const std::vector<not_null<UserData*>> &selected)
|
||||
: _titleFactory(std::move(titleFactory))
|
||||
, _selected(selected) {
|
||||
}
|
||||
@@ -163,11 +163,11 @@ int EditPrivacyBox::countDefaultHeight(int newWidth) {
|
||||
}
|
||||
|
||||
void EditPrivacyBox::editExceptionUsers(Exception exception) {
|
||||
auto controller = std::make_unique<PrivacyExceptionsBoxController>(base::lambda_guarded(this, [this, exception] {
|
||||
auto controller = std::make_unique<PrivacyExceptionsBoxController>(crl::guard(this, [this, exception] {
|
||||
return _controller->exceptionBoxTitle(exception);
|
||||
}), exceptionUsers(exception));
|
||||
auto initBox = [this, exception, controller = controller.get()](not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_settings_save), base::lambda_guarded(this, [this, box, exception, controller] {
|
||||
box->addButton(langFactory(lng_settings_save), crl::guard(this, [this, box, exception, controller] {
|
||||
exceptionUsers(exception) = controller->getResult();
|
||||
exceptionLink(exception)->entity()->setText(exceptionLinkText(exception));
|
||||
auto removeFrom = ([exception] {
|
||||
@@ -295,7 +295,7 @@ void EditPrivacyBox::createWidgets() {
|
||||
clearButtons();
|
||||
addButton(langFactory(lng_settings_save), [this] {
|
||||
auto someAreDisallowed = (_option != Option::Everyone) || !_neverUsers.empty();
|
||||
_controller->confirmSave(someAreDisallowed, base::lambda_guarded(this, [this] {
|
||||
_controller->confirmSave(someAreDisallowed, crl::guard(this, [this] {
|
||||
Auth().api().savePrivacy(_controller->key(), collectResult());
|
||||
closeBox();
|
||||
}));
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
virtual QString exceptionBoxTitle(Exception exception) = 0;
|
||||
virtual QString exceptionsDescription() = 0;
|
||||
|
||||
virtual void confirmSave(bool someAreDisallowed, base::lambda_once<void()> saveCallback) {
|
||||
virtual void confirmSave(bool someAreDisallowed, FnMut<void()> saveCallback) {
|
||||
saveCallback();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
|
||||
#include "boxes/mute_settings_box.h"
|
||||
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "auth_session.h"
|
||||
#include "data/data_session.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
@@ -31,7 +32,6 @@ void MuteSettingsBox::prepare() {
|
||||
|
||||
const auto icon = object_ptr<Ui::UserpicButton>(
|
||||
this,
|
||||
controller(),
|
||||
_peer,
|
||||
Ui::UserpicButton::Role::Custom,
|
||||
st::mutePhotoButton);
|
||||
@@ -69,10 +69,8 @@ void MuteSettingsBox::prepare() {
|
||||
|
||||
addButton(langFactory(lng_box_ok), [this, group] {
|
||||
auto muteForSeconds = group->value() * 3600;
|
||||
App::main()->updateNotifySettings(
|
||||
Auth().data().updateNotifySettings(
|
||||
_peer,
|
||||
Data::NotifySettings::MuteChange::Mute,
|
||||
Data::NotifySettings::SilentPostsChange::Ignore,
|
||||
muteForSeconds);
|
||||
closeBox();
|
||||
});
|
||||
|
||||
@@ -7,13 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "boxes/passcode_box.h"
|
||||
|
||||
#include "base/bytes.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "mainwindow.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "passport/passport_encryption.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
|
||||
: _turningOff(turningOff)
|
||||
@@ -26,12 +28,22 @@ PasscodeBox::PasscodeBox(QWidget*, bool turningOff)
|
||||
, _recover(this, lang(lng_signin_recover)) {
|
||||
}
|
||||
|
||||
PasscodeBox::PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff)
|
||||
PasscodeBox::PasscodeBox(
|
||||
QWidget*,
|
||||
const QByteArray &newSalt,
|
||||
const QByteArray &curSalt,
|
||||
bool hasRecovery,
|
||||
bool notEmptyPassport,
|
||||
const QString &hint,
|
||||
const QByteArray &newSecureSecretSalt,
|
||||
bool turningOff)
|
||||
: _turningOff(turningOff)
|
||||
, _cloudPwd(true)
|
||||
, _newSalt(newSalt)
|
||||
, _curSalt(curSalt)
|
||||
, _newSecureSecretSalt(newSecureSecretSalt)
|
||||
, _hasRecovery(hasRecovery)
|
||||
, _notEmptyPassport(notEmptyPassport)
|
||||
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
|
||||
, _oldPasscode(this, st::defaultInputField, langFactory(lng_cloud_password_enter_old))
|
||||
, _newPasscode(this, st::defaultInputField, langFactory(curSalt.isEmpty() ? lng_cloud_password_enter_first : lng_cloud_password_enter_new))
|
||||
@@ -43,8 +55,8 @@ PasscodeBox::PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray &
|
||||
}
|
||||
|
||||
void PasscodeBox::prepare() {
|
||||
addButton(langFactory(_turningOff ? lng_passcode_remove_button : lng_settings_save), [this] { onSave(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(_turningOff ? lng_passcode_remove_button : lng_settings_save), [=] { save(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
_about.setRichText(st::passcodeTextStyle, lang(_cloudPwd ? lng_cloud_password_about : lng_passcode_about));
|
||||
_aboutHeight = _about.countHeight(st::boxWidth - st::boxPadding.left() * 1.5);
|
||||
@@ -65,19 +77,20 @@ void PasscodeBox::prepare() {
|
||||
}
|
||||
}
|
||||
|
||||
connect(_oldPasscode, SIGNAL(changed()), this, SLOT(onOldChanged()));
|
||||
connect(_newPasscode, SIGNAL(changed()), this, SLOT(onNewChanged()));
|
||||
connect(_reenterPasscode, SIGNAL(changed()), this, SLOT(onNewChanged()));
|
||||
connect(_passwordHint, SIGNAL(changed()), this, SLOT(onNewChanged()));
|
||||
connect(_recoverEmail, SIGNAL(changed()), this, SLOT(onEmailChanged()));
|
||||
connect(_oldPasscode, &Ui::MaskedInputField::changed, [=] { oldChanged(); });
|
||||
connect(_newPasscode, &Ui::MaskedInputField::changed, [=] { newChanged(); });
|
||||
connect(_reenterPasscode, &Ui::MaskedInputField::changed, [=] { newChanged(); });
|
||||
connect(_passwordHint, &Ui::InputField::changed, [=] { newChanged(); });
|
||||
connect(_recoverEmail, &Ui::InputField::changed, [=] { emailChanged(); });
|
||||
|
||||
connect(_oldPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_newPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_reenterPasscode, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_passwordHint, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_recoverEmail, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
const auto fieldSubmit = [=] { submit(); };
|
||||
connect(_oldPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
|
||||
connect(_newPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
|
||||
connect(_reenterPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
|
||||
connect(_passwordHint, &Ui::InputField::submitted, fieldSubmit);
|
||||
connect(_recoverEmail, &Ui::InputField::submitted, fieldSubmit);
|
||||
|
||||
connect(_recover, SIGNAL(clicked()), this, SLOT(onRecoverByEmail()));
|
||||
_recover->addClickHandler([=] { recoverByEmail(); });
|
||||
|
||||
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
|
||||
_oldPasscode->setVisible(_turningOff || has);
|
||||
@@ -88,11 +101,11 @@ void PasscodeBox::prepare() {
|
||||
_recoverEmail->setVisible(!_turningOff && _cloudPwd && _curSalt.isEmpty());
|
||||
}
|
||||
|
||||
void PasscodeBox::onSubmit() {
|
||||
void PasscodeBox::submit() {
|
||||
bool has = _cloudPwd ? (!_curSalt.isEmpty()) : Global::LocalPasscode();
|
||||
if (_oldPasscode->hasFocus()) {
|
||||
if (_turningOff) {
|
||||
onSave();
|
||||
save();
|
||||
} else {
|
||||
_newPasscode->setFocus();
|
||||
}
|
||||
@@ -110,16 +123,16 @@ void PasscodeBox::onSubmit() {
|
||||
} else if (!_passwordHint->isHidden()) {
|
||||
_passwordHint->setFocus();
|
||||
} else {
|
||||
onSave();
|
||||
save();
|
||||
}
|
||||
} else if (_passwordHint->hasFocus()) {
|
||||
if (_recoverEmail->isHidden()) {
|
||||
onSave();
|
||||
save();
|
||||
} else {
|
||||
_recoverEmail->setFocus();
|
||||
}
|
||||
} else if (_recoverEmail->hasFocus()) {
|
||||
onSave();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,11 +197,11 @@ void PasscodeBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::setPasswordDone(const MTPBool &result) {
|
||||
void PasscodeBox::setPasswordDone() {
|
||||
_setRequest = 0;
|
||||
emit reloadPassword();
|
||||
auto text = lang(_reenterPasscode->isHidden() ? lng_cloud_password_removed : (_oldPasscode->isHidden() ? lng_cloud_password_was_set : lng_cloud_password_updated));
|
||||
Ui::show(Box<InformBox>(text));
|
||||
getDelegate()->show(Box<InformBox>(text), LayerOption::CloseOther);
|
||||
}
|
||||
|
||||
void PasscodeBox::closeReplacedBy() {
|
||||
@@ -226,7 +239,7 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
|
||||
emit reloadPassword();
|
||||
closeBox();
|
||||
} else {
|
||||
onBadOldPasscode();
|
||||
badOldPasscode();
|
||||
}
|
||||
} else if (err == qstr("NEW_PASSWORD_BAD")) {
|
||||
_newPasscode->setFocus();
|
||||
@@ -242,13 +255,15 @@ bool PasscodeBox::setPasswordFail(const RPCError &error) {
|
||||
_recoverEmail->showError();
|
||||
update();
|
||||
} else if (err == qstr("EMAIL_UNCONFIRMED")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_cloud_password_almost)));
|
||||
getDelegate()->show(
|
||||
Box<InformBox>(lang(lng_cloud_password_almost)),
|
||||
LayerOption::CloseOther);
|
||||
emit reloadPassword();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PasscodeBox::onSave(bool force) {
|
||||
void PasscodeBox::save(bool force) {
|
||||
if (_setRequest) return;
|
||||
|
||||
QString old = _oldPasscode->text(), pwd = _newPasscode->text(), conf = _reenterPasscode->text();
|
||||
@@ -268,7 +283,7 @@ void PasscodeBox::onSave(bool force) {
|
||||
} else {
|
||||
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
|
||||
cSetPasscodeLastTry(getms(true));
|
||||
onBadOldPasscode();
|
||||
badOldPasscode();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -305,29 +320,15 @@ void PasscodeBox::onSave(bool force) {
|
||||
}
|
||||
if (!_recoverEmail->isHidden() && email.isEmpty() && !force) {
|
||||
_skipEmailWarning = true;
|
||||
_replacedBy = Ui::show(Box<ConfirmBox>(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton, base::lambda_guarded(this, [this] {
|
||||
onSave(true);
|
||||
})), LayerOption::KeepOther);
|
||||
_replacedBy = getDelegate()->show(Box<ConfirmBox>(lang(lng_cloud_password_about_recover), lang(lng_cloud_password_skip_email), st::attentionBoxButton, crl::guard(this, [this] {
|
||||
save(true);
|
||||
})));
|
||||
} else if (_newPasscode->isHidden()) {
|
||||
clearCloudPassword(old);
|
||||
} else if (_oldPasscode->isHidden()) {
|
||||
setNewCloudPassword(pwd);
|
||||
} else {
|
||||
QByteArray newPasswordData = pwd.isEmpty() ? QByteArray() : (_newSalt + pwd.toUtf8() + _newSalt);
|
||||
QByteArray newPasswordHash = pwd.isEmpty() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
|
||||
if (pwd.isEmpty()) {
|
||||
hint = QString();
|
||||
email = QString();
|
||||
} else {
|
||||
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
|
||||
}
|
||||
QByteArray oldPasswordData = _oldPasscode->isHidden() ? QByteArray() : (_curSalt + old.toUtf8() + _curSalt);
|
||||
QByteArray oldPasswordHash = _oldPasscode->isHidden() ? QByteArray() : QByteArray(32, Qt::Uninitialized);
|
||||
if (!_oldPasscode->isHidden()) {
|
||||
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
|
||||
}
|
||||
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt | MTPDaccount_passwordInputSettings::Flag::f_new_password_hash | MTPDaccount_passwordInputSettings::Flag::f_hint;
|
||||
if (_oldPasscode->isHidden() || _newPasscode->isHidden()) {
|
||||
flags |= MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||
}
|
||||
MTPaccount_PasswordInputSettings settings(MTP_account_passwordInputSettings(MTP_flags(flags), MTP_bytes(_newSalt), MTP_bytes(newPasswordHash), MTP_string(hint), MTP_string(email)));
|
||||
_setRequest = MTP::send(MTPaccount_UpdatePasswordSettings(MTP_bytes(oldPasswordHash), settings), rpcDone(&PasscodeBox::setPasswordDone), rpcFail(&PasscodeBox::setPasswordFail));
|
||||
changeCloudPassword(old, pwd);
|
||||
}
|
||||
} else {
|
||||
cSetPasscodeBadTries(0);
|
||||
@@ -337,7 +338,213 @@ void PasscodeBox::onSave(bool force) {
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::onBadOldPasscode() {
|
||||
void PasscodeBox::clearCloudPassword(const QString &oldPassword) {
|
||||
Expects(!_oldPasscode->isHidden());
|
||||
|
||||
const auto send = [=] {
|
||||
sendClearCloudPassword(oldPassword);
|
||||
};
|
||||
if (_notEmptyPassport) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto confirmed = [=] {
|
||||
send();
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
lang(lng_cloud_password_passport_losing),
|
||||
lang(lng_continue),
|
||||
confirmed));
|
||||
} else {
|
||||
send();
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::sendClearCloudPassword(const QString &oldPassword) {
|
||||
const auto passwordUtf = oldPassword.toUtf8();
|
||||
const auto oldPasswordData = (_curSalt + passwordUtf + _curSalt);
|
||||
auto oldPasswordHash = QByteArray(32, Qt::Uninitialized);
|
||||
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
|
||||
const auto newPasswordData = QByteArray();
|
||||
const auto newPasswordHash = QByteArray();
|
||||
const auto hint = QString();
|
||||
const auto email = QString();
|
||||
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
MTP_bytes(_newSalt),
|
||||
MTP_bytes(newPasswordHash),
|
||||
MTP_string(hint),
|
||||
MTP_string(email),
|
||||
MTPbytes(), // new_secure_salt
|
||||
MTPbytes(), // new_secure_secret
|
||||
MTPlong()) // new_secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone();
|
||||
}).fail([=](const RPCError &error) {
|
||||
setPasswordFail(error);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
|
||||
const auto newPasswordData = (_newSalt + newPassword.toUtf8() + _newSalt);
|
||||
auto newPasswordHash = QByteArray(32, Qt::Uninitialized);
|
||||
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
|
||||
const auto oldPasswordData = QByteArray();
|
||||
const auto oldPasswordHash = QByteArray();
|
||||
const auto hint = _passwordHint->getLastText();
|
||||
const auto email = _recoverEmail->getLastText().trimmed();
|
||||
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_hint
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
MTP_bytes(_newSalt),
|
||||
MTP_bytes(newPasswordHash),
|
||||
MTP_string(hint),
|
||||
MTP_string(email),
|
||||
MTPbytes(), // new_secure_salt
|
||||
MTPbytes(), // new_secure_secret
|
||||
MTPlong()) // new_secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone();
|
||||
}).fail([=](const RPCError &error) {
|
||||
setPasswordFail(error);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::changeCloudPassword(
|
||||
const QString &oldPassword,
|
||||
const QString &newPassword) {
|
||||
const auto passwordUtf = oldPassword.toUtf8();
|
||||
const auto oldPasswordData = (_curSalt + passwordUtf + _curSalt);
|
||||
auto oldPasswordHash = QByteArray(32, Qt::Uninitialized);
|
||||
hashSha256(oldPasswordData.constData(), oldPasswordData.size(), oldPasswordHash.data());
|
||||
_setRequest = request(MTPaccount_GetPasswordSettings(
|
||||
MTP_bytes(oldPasswordHash)
|
||||
)).done([=](const MTPaccount_PasswordSettings &result) {
|
||||
_setRequest = 0;
|
||||
|
||||
Expects(result.type() == mtpc_account_passwordSettings);
|
||||
const auto &data = result.c_account_passwordSettings();
|
||||
|
||||
if (data.vsecure_secret.v.isEmpty()) {
|
||||
const auto empty = QByteArray();
|
||||
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
|
||||
return;
|
||||
}
|
||||
const auto secret = Passport::DecryptSecureSecret(
|
||||
bytes::make_span(data.vsecure_salt.v),
|
||||
bytes::make_span(data.vsecure_secret.v),
|
||||
bytes::make_span(passwordUtf));
|
||||
if (secret.empty()) {
|
||||
LOG(("API Error: Failed to decrypt secure secret."));
|
||||
suggestSecretReset(oldPasswordHash, newPassword);
|
||||
} else if (Passport::CountSecureSecretId(secret) != data.vsecure_secret_id.v) {
|
||||
LOG(("API Error: Wrong secure secret id."));
|
||||
suggestSecretReset(oldPasswordHash, newPassword);
|
||||
} else {
|
||||
sendChangeCloudPassword(
|
||||
oldPasswordHash,
|
||||
newPassword,
|
||||
QByteArray::fromRawData(
|
||||
reinterpret_cast<const char*>(secret.data()),
|
||||
secret.size()));
|
||||
}
|
||||
}).fail([=](const RPCError &error) {
|
||||
setPasswordFail(error);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::suggestSecretReset(
|
||||
const QByteArray &oldPasswordHash,
|
||||
const QString &newPassword) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto resetSecretAndSave = [=] {
|
||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(Flag::f_new_secure_salt
|
||||
| Flag::f_new_secure_secret
|
||||
| Flag::f_new_secure_secret_id),
|
||||
MTPbytes(), // new_salt
|
||||
MTPbytes(), // new_password_hash
|
||||
MTPstring(), // hint
|
||||
MTPstring(), // email
|
||||
MTP_bytes(QByteArray()), // new_secure_salt
|
||||
MTP_bytes(QByteArray()), // new_secure_secret
|
||||
MTP_long(0)) // new_secure_secret_id
|
||||
)).done([=](const MTPBool &result) {
|
||||
_setRequest = 0;
|
||||
const auto empty = QByteArray();
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
sendChangeCloudPassword(oldPasswordHash, newPassword, empty);
|
||||
}).fail([=](const RPCError &error) {
|
||||
_setRequest = 0;
|
||||
}).send();
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
Lang::Hard::PassportCorrupted(),
|
||||
Lang::Hard::PassportCorruptedReset(),
|
||||
[=] { resetSecretAndSave(); }));
|
||||
}
|
||||
|
||||
void PasscodeBox::sendChangeCloudPassword(
|
||||
const QByteArray &oldPasswordHash,
|
||||
const QString &newPassword,
|
||||
const QByteArray &secureSecret) {
|
||||
const auto passwordUtf = newPassword.toUtf8();
|
||||
const auto newPasswordData = (_newSalt + passwordUtf + _newSalt);
|
||||
auto newPasswordHash = QByteArray(32, Qt::Uninitialized);
|
||||
hashSha256(newPasswordData.constData(), newPasswordData.size(), newPasswordHash.data());
|
||||
const auto hint = _passwordHint->getLastText();
|
||||
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_salt
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_hint;
|
||||
auto newSecureSecret = bytes::vector();
|
||||
auto newSecureSecretId = 0ULL;
|
||||
if (!secureSecret.isEmpty()) {
|
||||
flags |= MTPDaccount_passwordInputSettings::Flag::f_new_secure_salt
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_secure_secret
|
||||
| MTPDaccount_passwordInputSettings::Flag::f_new_secure_secret_id;
|
||||
newSecureSecretId = Passport::CountSecureSecretId(
|
||||
bytes::make_span(secureSecret));
|
||||
newSecureSecret = Passport::EncryptSecureSecret(
|
||||
bytes::make_span(_newSecureSecretSalt),
|
||||
bytes::make_span(secureSecret),
|
||||
bytes::make_span(passwordUtf));
|
||||
}
|
||||
_setRequest = request(MTPaccount_UpdatePasswordSettings(
|
||||
MTP_bytes(oldPasswordHash),
|
||||
MTP_account_passwordInputSettings(
|
||||
MTP_flags(flags),
|
||||
MTP_bytes(_newSalt),
|
||||
MTP_bytes(newPasswordHash),
|
||||
MTP_string(hint),
|
||||
MTPstring(), // email is not changing
|
||||
MTP_bytes(_newSecureSecretSalt),
|
||||
MTP_bytes(newSecureSecret),
|
||||
MTP_long(newSecureSecretId))
|
||||
)).done([=](const MTPBool &result) {
|
||||
setPasswordDone();
|
||||
}).fail([=](const RPCError &error) {
|
||||
setPasswordFail(error);
|
||||
}).send();
|
||||
}
|
||||
|
||||
void PasscodeBox::badOldPasscode() {
|
||||
_oldPasscode->selectAll();
|
||||
_oldPasscode->setFocus();
|
||||
_oldPasscode->showError();
|
||||
@@ -348,7 +555,7 @@ void PasscodeBox::onBadOldPasscode() {
|
||||
update();
|
||||
}
|
||||
|
||||
void PasscodeBox::onOldChanged() {
|
||||
void PasscodeBox::oldChanged() {
|
||||
if (!_oldError.isEmpty()) {
|
||||
_oldError = QString();
|
||||
if (_hasRecovery && _hintText.isEmpty()) {
|
||||
@@ -358,41 +565,47 @@ void PasscodeBox::onOldChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::onNewChanged() {
|
||||
void PasscodeBox::newChanged() {
|
||||
if (!_newError.isEmpty()) {
|
||||
_newError = QString();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::onEmailChanged() {
|
||||
void PasscodeBox::emailChanged() {
|
||||
if (!_emailError.isEmpty()) {
|
||||
_emailError = QString();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::onRecoverByEmail() {
|
||||
void PasscodeBox::recoverByEmail() {
|
||||
if (_pattern.isEmpty()) {
|
||||
_pattern = "-";
|
||||
MTP::send(MTPauth_RequestPasswordRecovery(), rpcDone(&PasscodeBox::recoverStarted), rpcFail(&PasscodeBox::recoverStartFail));
|
||||
request(MTPauth_RequestPasswordRecovery(
|
||||
)).done([=](const MTPauth_PasswordRecovery &result) {
|
||||
recoverStarted(result);
|
||||
}).fail([=](const RPCError &error) {
|
||||
recoverStartFail(error);
|
||||
}).send();
|
||||
} else {
|
||||
recover();
|
||||
}
|
||||
}
|
||||
|
||||
void PasscodeBox::onRecoverExpired() {
|
||||
void PasscodeBox::recoverExpired() {
|
||||
_pattern = QString();
|
||||
}
|
||||
|
||||
void PasscodeBox::recover() {
|
||||
if (_pattern == "-") return;
|
||||
|
||||
_replacedBy = Ui::show(
|
||||
Box<RecoverBox>(_pattern),
|
||||
LayerOption::KeepOther);
|
||||
connect(_replacedBy, SIGNAL(reloadPassword()), this, SIGNAL(reloadPassword()));
|
||||
connect(_replacedBy, SIGNAL(recoveryExpired()), this, SLOT(onRecoverExpired()));
|
||||
const auto box = getDelegate()->show(Box<RecoverBox>(
|
||||
_pattern,
|
||||
_notEmptyPassport));
|
||||
connect(box, &RecoverBox::reloadPassword, this, &PasscodeBox::reloadPassword);
|
||||
connect(box, &RecoverBox::recoveryExpired, this, &PasscodeBox::recoverExpired);
|
||||
_replacedBy = box;
|
||||
}
|
||||
|
||||
void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
|
||||
@@ -408,21 +621,25 @@ bool PasscodeBox::recoverStartFail(const RPCError &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RecoverBox::RecoverBox(QWidget*, const QString &pattern)
|
||||
RecoverBox::RecoverBox(
|
||||
QWidget*,
|
||||
const QString &pattern,
|
||||
bool notEmptyPassport)
|
||||
: _pattern(st::normalFont->elided(lng_signin_recover_hint(lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
|
||||
, _notEmptyPassport(notEmptyPassport)
|
||||
, _recoverCode(this, st::defaultInputField, langFactory(lng_signin_code)) {
|
||||
}
|
||||
|
||||
void RecoverBox::prepare() {
|
||||
setTitle(langFactory(lng_signin_recover_title));
|
||||
|
||||
addButton(langFactory(lng_passcode_submit), [this] { onSubmit(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_passcode_submit), [=] { submit(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
setDimensions(st::boxWidth, st::passcodePadding.top() + st::passcodePadding.bottom() + st::passcodeTextLine + _recoverCode->height() + st::passcodeTextLine);
|
||||
|
||||
connect(_recoverCode, SIGNAL(changed()), this, SLOT(onCodeChanged()));
|
||||
connect(_recoverCode, SIGNAL(submitted(bool)), this, SLOT(onSubmit()));
|
||||
connect(_recoverCode, &Ui::InputField::changed, [=] { codeChanged(); });
|
||||
connect(_recoverCode, &Ui::InputField::submitted, [=] { submit(); });
|
||||
}
|
||||
|
||||
void RecoverBox::paintEvent(QPaintEvent *e) {
|
||||
@@ -452,7 +669,7 @@ void RecoverBox::setInnerFocus() {
|
||||
_recoverCode->setFocusFast();
|
||||
}
|
||||
|
||||
void RecoverBox::onSubmit() {
|
||||
void RecoverBox::submit() {
|
||||
if (_submitRequest) return;
|
||||
|
||||
QString code = _recoverCode->getLastText().trimmed();
|
||||
@@ -462,10 +679,30 @@ void RecoverBox::onSubmit() {
|
||||
return;
|
||||
}
|
||||
|
||||
_submitRequest = MTP::send(MTPauth_RecoverPassword(MTP_string(code)), rpcDone(&RecoverBox::codeSubmitDone, true), rpcFail(&RecoverBox::codeSubmitFail));
|
||||
const auto send = crl::guard(this, [=] {
|
||||
_submitRequest = MTP::send(
|
||||
MTPauth_RecoverPassword(MTP_string(code)),
|
||||
rpcDone(&RecoverBox::codeSubmitDone, true),
|
||||
rpcFail(&RecoverBox::codeSubmitFail));
|
||||
});
|
||||
if (_notEmptyPassport) {
|
||||
const auto box = std::make_shared<QPointer<BoxContent>>();
|
||||
const auto confirmed = [=] {
|
||||
send();
|
||||
if (*box) {
|
||||
(*box)->closeBox();
|
||||
}
|
||||
};
|
||||
*box = getDelegate()->show(Box<ConfirmBox>(
|
||||
lang(lng_cloud_password_passport_losing),
|
||||
lang(lng_continue),
|
||||
confirmed));
|
||||
} else {
|
||||
send();
|
||||
}
|
||||
}
|
||||
|
||||
void RecoverBox::onCodeChanged() {
|
||||
void RecoverBox::codeChanged() {
|
||||
_error = QString();
|
||||
update();
|
||||
}
|
||||
@@ -474,7 +711,9 @@ void RecoverBox::codeSubmitDone(bool recover, const MTPauth_Authorization &resul
|
||||
_submitRequest = 0;
|
||||
|
||||
emit reloadPassword();
|
||||
Ui::show(Box<InformBox>(lang(lng_cloud_password_removed)));
|
||||
getDelegate()->show(
|
||||
Box<InformBox>(lang(lng_cloud_password_removed)),
|
||||
LayerOption::CloseOther);
|
||||
}
|
||||
|
||||
bool RecoverBox::codeSubmitFail(const RPCError &error) {
|
||||
@@ -492,7 +731,9 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
|
||||
const QString &err = error.type();
|
||||
if (err == qstr("PASSWORD_EMPTY")) {
|
||||
emit reloadPassword();
|
||||
Ui::show(Box<InformBox>(lang(lng_cloud_password_removed)));
|
||||
getDelegate()->show(
|
||||
Box<InformBox>(lang(lng_cloud_password_removed)),
|
||||
LayerOption::CloseOther);
|
||||
return true;
|
||||
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
|
||||
closeBox();
|
||||
@@ -509,7 +750,7 @@ bool RecoverBox::codeSubmitFail(const RPCError &error) {
|
||||
_recoverCode->showError();
|
||||
return true;
|
||||
}
|
||||
if (cDebug()) { // internal server error
|
||||
if (Logs::DebugEnabled()) { // internal server error
|
||||
_error = err + ": " + error.description();
|
||||
} else {
|
||||
_error = Lang::Hard::ServerError();
|
||||
|
||||
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Ui {
|
||||
class InputField;
|
||||
@@ -15,22 +16,20 @@ class PasswordInput;
|
||||
class LinkButton;
|
||||
} // namespace Ui
|
||||
|
||||
class PasscodeBox : public BoxContent, public RPCSender {
|
||||
class PasscodeBox : public BoxContent, private MTP::Sender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PasscodeBox(QWidget*, bool turningOff);
|
||||
PasscodeBox(QWidget*, const QByteArray &newSalt, const QByteArray &curSalt, bool hasRecovery, const QString &hint, bool turningOff = false);
|
||||
|
||||
private slots:
|
||||
void onSave(bool force = false);
|
||||
void onBadOldPasscode();
|
||||
void onOldChanged();
|
||||
void onNewChanged();
|
||||
void onEmailChanged();
|
||||
void onRecoverByEmail();
|
||||
void onRecoverExpired();
|
||||
void onSubmit();
|
||||
PasscodeBox(
|
||||
QWidget*,
|
||||
const QByteArray &newSalt,
|
||||
const QByteArray &curSalt,
|
||||
bool hasRecovery,
|
||||
bool notEmptyPassport,
|
||||
const QString &hint,
|
||||
const QByteArray &newSecureSecretSalt,
|
||||
bool turningOff = false);
|
||||
|
||||
signals:
|
||||
void reloadPassword();
|
||||
@@ -43,15 +42,40 @@ protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
void submit();
|
||||
void closeReplacedBy();
|
||||
void oldChanged();
|
||||
void newChanged();
|
||||
void emailChanged();
|
||||
void save(bool force = false);
|
||||
void badOldPasscode();
|
||||
void recoverByEmail();
|
||||
void recoverExpired();
|
||||
|
||||
void setPasswordDone(const MTPBool &result);
|
||||
void setPasswordDone();
|
||||
bool setPasswordFail(const RPCError &error);
|
||||
|
||||
void recoverStarted(const MTPauth_PasswordRecovery &result);
|
||||
bool recoverStartFail(const RPCError &error);
|
||||
|
||||
void recover();
|
||||
void clearCloudPassword(const QString &oldPassword);
|
||||
void setNewCloudPassword(const QString &newPassword);
|
||||
void changeCloudPassword(
|
||||
const QString &oldPassword,
|
||||
const QString &newPassword);
|
||||
void sendChangeCloudPassword(
|
||||
const QByteArray &oldPasswordHash,
|
||||
const QString &newPassword,
|
||||
const QByteArray &secureSecret);
|
||||
void suggestSecretReset(
|
||||
const QByteArray &oldPasswordHash,
|
||||
const QString &newPassword);
|
||||
void resetSecretAndChangePassword(
|
||||
const QByteArray &oldPasswordHash,
|
||||
const QString &newPassword);
|
||||
void sendClearCloudPassword(const QString &oldPassword);
|
||||
|
||||
QString _pattern;
|
||||
|
||||
QPointer<BoxContent> _replacedBy;
|
||||
@@ -59,8 +83,9 @@ private:
|
||||
bool _cloudPwd = false;
|
||||
mtpRequestId _setRequest = 0;
|
||||
|
||||
QByteArray _newSalt, _curSalt;
|
||||
QByteArray _newSalt, _curSalt, _newSecureSecretSalt;
|
||||
bool _hasRecovery = false;
|
||||
bool _notEmptyPassport = false;
|
||||
bool _skipEmailWarning = false;
|
||||
|
||||
int _aboutHeight = 0;
|
||||
@@ -82,11 +107,7 @@ class RecoverBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RecoverBox(QWidget*, const QString &pattern);
|
||||
|
||||
public slots:
|
||||
void onSubmit();
|
||||
void onCodeChanged();
|
||||
RecoverBox(QWidget*, const QString &pattern, bool notEmptyPassport);
|
||||
|
||||
signals:
|
||||
void reloadPassword();
|
||||
@@ -100,12 +121,15 @@ protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
void submit();
|
||||
void codeChanged();
|
||||
void codeSubmitDone(bool recover, const MTPauth_Authorization &result);
|
||||
bool codeSubmitFail(const RPCError &error);
|
||||
|
||||
mtpRequestId _submitRequest = 0;
|
||||
|
||||
QString _pattern;
|
||||
bool _notEmptyPassport = false;
|
||||
|
||||
object_ptr<Ui::InputField> _recoverCode;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
PeerListBox::PeerListBox(
|
||||
QWidget*,
|
||||
std::unique_ptr<PeerListController> controller,
|
||||
base::lambda<void(not_null<PeerListBox*>)> init)
|
||||
Fn<void(not_null<PeerListBox*>)> init)
|
||||
: _controller(std::move(controller))
|
||||
, _init(std::move(init)) {
|
||||
Expects(_controller != nullptr);
|
||||
@@ -46,7 +46,7 @@ void PeerListBox::createMultiSelect() {
|
||||
) | rpl::start_with_next(
|
||||
[this] { updateScrollSkips(); },
|
||||
lifetime());
|
||||
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { content()->submitted(); });
|
||||
_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 = App::peerLoaded(itemId)) {
|
||||
@@ -525,7 +525,7 @@ void PeerListRow::lazyInitialize(const style::PeerListItem &st) {
|
||||
refreshStatus();
|
||||
}
|
||||
|
||||
void PeerListRow::createCheckbox(base::lambda<void()> updateCallback) {
|
||||
void PeerListRow::createCheckbox(Fn<void()> updateCallback) {
|
||||
_checkbox = std::make_unique<Ui::RoundImageCheckbox>(
|
||||
st::contactsPhotoCheckbox,
|
||||
std::move(updateCallback),
|
||||
@@ -1022,7 +1022,7 @@ void PeerListContent::contextMenuEvent(QContextMenuEvent *e) {
|
||||
if (const auto row = getRow(_contexted.index)) {
|
||||
_contextMenu = _controller->rowContextMenu(row);
|
||||
if (_contextMenu) {
|
||||
_contextMenu->setDestroyedCallback(base::lambda_guarded(
|
||||
_contextMenu->setDestroyedCallback(crl::guard(
|
||||
this,
|
||||
[this] {
|
||||
setContexted(Selected());
|
||||
@@ -1564,3 +1564,9 @@ void PeerListContent::handleNameChanged(const Notify::PeerUpdate &update) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PeerListContent::~PeerListContent() {
|
||||
if (_contextMenu) {
|
||||
_contextMenu->setDestroyedCallback(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ struct PeerUpdate;
|
||||
inline auto PaintUserpicCallback(
|
||||
not_null<PeerData*> peer,
|
||||
bool respectSavedMessagesChat)
|
||||
->base::lambda<void(Painter &p, int x, int y, int outerWidth, int size)> {
|
||||
->Fn<void(Painter &p, int x, int y, int outerWidth, int size)> {
|
||||
if (respectSavedMessagesChat && peer->isSelf()) {
|
||||
return [](Painter &p, int x, int y, int outerWidth, int size) {
|
||||
Ui::EmptyUserpic::PaintSavedMessages(p, x, y, outerWidth, size);
|
||||
@@ -95,7 +95,7 @@ public:
|
||||
virtual QMargins actionMargins() const {
|
||||
return QMargins();
|
||||
}
|
||||
virtual void addActionRipple(QPoint point, base::lambda<void()> updateCallback) {
|
||||
virtual void addActionRipple(QPoint point, Fn<void()> updateCallback) {
|
||||
}
|
||||
virtual void stopLastActionRipple() {
|
||||
}
|
||||
@@ -200,7 +200,7 @@ protected:
|
||||
}
|
||||
|
||||
private:
|
||||
void createCheckbox(base::lambda<void()> updateCallback);
|
||||
void createCheckbox(Fn<void()> updateCallback);
|
||||
void setCheckedInternal(bool checked, SetStyle style);
|
||||
void paintDisabledCheckUserpic(
|
||||
Painter &p,
|
||||
@@ -236,8 +236,8 @@ struct PeerListState;
|
||||
|
||||
class PeerListDelegate {
|
||||
public:
|
||||
virtual void peerListSetTitle(base::lambda<QString()> title) = 0;
|
||||
virtual void peerListSetAdditionalTitle(base::lambda<QString()> title) = 0;
|
||||
virtual void peerListSetTitle(Fn<QString()> title) = 0;
|
||||
virtual void peerListSetAdditionalTitle(Fn<QString()> title) = 0;
|
||||
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
|
||||
virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
|
||||
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
|
||||
@@ -258,8 +258,8 @@ public:
|
||||
virtual void peerListScrollToTop() = 0;
|
||||
virtual int peerListFullRowsCount() = 0;
|
||||
virtual PeerListRow *peerListFindRow(PeerListRowId id) = 0;
|
||||
virtual void peerListSortRows(base::lambda<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0;
|
||||
virtual int peerListPartitionRows(base::lambda<bool(const PeerListRow &a)> border) = 0;
|
||||
virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0;
|
||||
virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
|
||||
|
||||
template <typename PeerDataRange>
|
||||
void peerListAddSelectedRows(PeerDataRange &&range) {
|
||||
@@ -484,6 +484,8 @@ public:
|
||||
return _scrollToRequests.events();
|
||||
}
|
||||
|
||||
~PeerListContent();
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void visibleTopBottomUpdated(
|
||||
@@ -699,7 +701,7 @@ public:
|
||||
_content->setSearchMode(mode);
|
||||
}
|
||||
void peerListSortRows(
|
||||
base::lambda<bool(const PeerListRow &a, const PeerListRow &b)> compare) override {
|
||||
Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) override {
|
||||
_content->reorderRows([&](
|
||||
auto &&begin,
|
||||
auto &&end) {
|
||||
@@ -709,7 +711,7 @@ public:
|
||||
});
|
||||
}
|
||||
int peerListPartitionRows(
|
||||
base::lambda<bool(const PeerListRow &a)> border) override {
|
||||
Fn<bool(const PeerListRow &a)> border) override {
|
||||
auto result = 0;
|
||||
_content->reorderRows([&](
|
||||
auto &&begin,
|
||||
@@ -747,13 +749,13 @@ public:
|
||||
PeerListBox(
|
||||
QWidget*,
|
||||
std::unique_ptr<PeerListController> controller,
|
||||
base::lambda<void(not_null<PeerListBox*>)> init);
|
||||
Fn<void(not_null<PeerListBox*>)> init);
|
||||
|
||||
void peerListSetTitle(base::lambda<QString()> title) override {
|
||||
void peerListSetTitle(Fn<QString()> title) override {
|
||||
setTitle(std::move(title));
|
||||
}
|
||||
void peerListSetAdditionalTitle(
|
||||
base::lambda<QString()> title) override {
|
||||
Fn<QString()> title) override {
|
||||
setAdditionalTitle(std::move(title));
|
||||
}
|
||||
void peerListSetSearchMode(PeerListSearchMode mode) override;
|
||||
@@ -791,7 +793,7 @@ private:
|
||||
object_ptr<Ui::SlideWrap<Ui::MultiSelect>> _select = { nullptr };
|
||||
|
||||
std::unique_ptr<PeerListController> _controller;
|
||||
base::lambda<void(PeerListBox*)> _init;
|
||||
Fn<void(PeerListBox*)> _init;
|
||||
bool _scrollBottomFixed = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -507,7 +507,7 @@ void AddParticipantsBoxController::Start(
|
||||
base::flat_set<not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated) {
|
||||
auto initBox = [channel, justCreated](not_null<PeerListBox*> box) {
|
||||
auto subscription = std::make_shared<base::Subscription>();
|
||||
auto subscription = std::make_shared<rpl::lifetime>();
|
||||
box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
@@ -528,9 +528,9 @@ void AddParticipantsBoxController::Start(
|
||||
});
|
||||
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
|
||||
if (justCreated) {
|
||||
*subscription = box->boxClosing.add_subscription([channel] {
|
||||
box->boxClosing() | rpl::start_with_next([=] {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
});
|
||||
}, *subscription);
|
||||
}
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(channel, std::move(alreadyIn)), std::move(initBox)));
|
||||
@@ -901,7 +901,7 @@ void AddBotToGroupBoxController::prepareViewHook() {
|
||||
}
|
||||
|
||||
ChooseRecipientBoxController::ChooseRecipientBoxController(
|
||||
base::lambda_once<void(not_null<PeerData*>)> callback)
|
||||
FnMut<void(not_null<PeerData*>)> callback)
|
||||
: _callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ private:
|
||||
class ChooseRecipientBoxController : public ChatsListBoxController {
|
||||
public:
|
||||
ChooseRecipientBoxController(
|
||||
base::lambda_once<void(not_null<PeerData*>)> callback);
|
||||
FnMut<void(not_null<PeerData*>)> callback);
|
||||
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
|
||||
@@ -244,6 +244,6 @@ protected:
|
||||
not_null<History*> history) override;
|
||||
|
||||
private:
|
||||
base::lambda_once<void(not_null<PeerData*>)> _callback;
|
||||
FnMut<void(not_null<PeerData*>)> _callback;
|
||||
|
||||
};
|
||||
|
||||
@@ -38,11 +38,12 @@ namespace {
|
||||
|
||||
constexpr auto kUsernameCheckTimeout = TimeMs(200);
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
constexpr auto kMaxGroupChannelTitle = 255; // See also add_contact_box.
|
||||
constexpr auto kMaxChannelDescription = 255; // See also add_contact_box.
|
||||
|
||||
class Controller
|
||||
: private MTP::Sender
|
||||
, private base::has_weak_ptr {
|
||||
: public base::has_weak_ptr
|
||||
, private MTP::Sender {
|
||||
public:
|
||||
Controller(
|
||||
not_null<BoxContent*> box,
|
||||
@@ -71,7 +72,7 @@ private:
|
||||
};
|
||||
struct Controls {
|
||||
Ui::InputField *title = nullptr;
|
||||
Ui::InputArea *description = nullptr;
|
||||
Ui::InputField *description = nullptr;
|
||||
Ui::UserpicButton *photo = nullptr;
|
||||
rpl::lifetime initialPhotoImageWaiting;
|
||||
|
||||
@@ -100,7 +101,7 @@ private:
|
||||
base::optional<bool> everyoneInvites;
|
||||
};
|
||||
|
||||
base::lambda<QString()> computeTitle() const;
|
||||
Fn<QString()> computeTitle() const;
|
||||
object_ptr<Ui::RpWidget> createPhotoAndTitleEdit();
|
||||
object_ptr<Ui::RpWidget> createTitleEdit();
|
||||
object_ptr<Ui::RpWidget> createPhotoEdit();
|
||||
@@ -158,7 +159,7 @@ private:
|
||||
void saveInvites();
|
||||
void saveSignatures();
|
||||
void savePhoto();
|
||||
void pushSaveStage(base::lambda_once<void()> &&lambda);
|
||||
void pushSaveStage(FnMut<void()> &&lambda);
|
||||
void continueSave();
|
||||
void cancelSave();
|
||||
|
||||
@@ -173,7 +174,7 @@ private:
|
||||
UsernameState _usernameState = UsernameState::Normal;
|
||||
rpl::event_stream<rpl::producer<QString>> _usernameResultTexts;
|
||||
|
||||
std::deque<base::lambda_once<void()>> _saveStagesQueue;
|
||||
std::deque<FnMut<void()>> _saveStagesQueue;
|
||||
Saving _savingData;
|
||||
|
||||
};
|
||||
@@ -194,7 +195,7 @@ Controller::Controller(
|
||||
});
|
||||
}
|
||||
|
||||
base::lambda<QString()> Controller::computeTitle() const {
|
||||
Fn<QString()> Controller::computeTitle() const {
|
||||
return langFactory(_isGroup
|
||||
? lng_edit_group
|
||||
: lng_edit_channel_title);
|
||||
@@ -276,7 +277,6 @@ object_ptr<Ui::RpWidget> Controller::createPhotoEdit() {
|
||||
_wrap,
|
||||
object_ptr<Ui::UserpicButton>(
|
||||
_wrap,
|
||||
_box->controller(),
|
||||
_peer,
|
||||
Ui::UserpicButton::Role::ChangePhoto,
|
||||
st::defaultUserpicButton),
|
||||
@@ -299,11 +299,15 @@ object_ptr<Ui::RpWidget> Controller::createTitleEdit() {
|
||||
: lng_dlg_new_channel_name),
|
||||
_peer->name),
|
||||
st::editPeerTitleMargins);
|
||||
result->entity()->setMaxLength(kMaxGroupChannelTitle);
|
||||
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
result->entity()->setInstantReplacesEnabled(
|
||||
Global::ReplaceEmojiValue());
|
||||
|
||||
QObject::connect(
|
||||
result->entity(),
|
||||
&Ui::InputField::submitted,
|
||||
[this] { submitTitle(); });
|
||||
[=] { submitTitle(); });
|
||||
|
||||
_controls.title = result->entity();
|
||||
return std::move(result);
|
||||
@@ -317,20 +321,24 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto result = object_ptr<Ui::PaddingWrap<Ui::InputArea>>(
|
||||
auto result = object_ptr<Ui::PaddingWrap<Ui::InputField>>(
|
||||
_wrap,
|
||||
object_ptr<Ui::InputArea>(
|
||||
object_ptr<Ui::InputField>(
|
||||
_wrap,
|
||||
st::editPeerDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_create_group_description),
|
||||
channel->about()),
|
||||
st::editPeerDescriptionMargins);
|
||||
result->entity()->setMaxLength(kMaxChannelDescription);
|
||||
result->entity()->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
result->entity()->setInstantReplacesEnabled(
|
||||
Global::ReplaceEmojiValue());
|
||||
|
||||
QObject::connect(
|
||||
result->entity(),
|
||||
&Ui::InputArea::submitted,
|
||||
[this] { submitDescription(); });
|
||||
&Ui::InputField::submitted,
|
||||
[=] { submitDescription(); });
|
||||
|
||||
_controls.description = result->entity();
|
||||
return std::move(result);
|
||||
@@ -426,7 +434,7 @@ object_ptr<Ui::RpWidget> Controller::createUsernameEdit() {
|
||||
object_ptr<Ui::UsernameInput>(
|
||||
container,
|
||||
st::setupChannelLink,
|
||||
base::lambda<QString()>(),
|
||||
Fn<QString()>(),
|
||||
channel->username,
|
||||
true));
|
||||
_controls.username->heightValue(
|
||||
@@ -557,7 +565,7 @@ void Controller::checkUsernameAvailability() {
|
||||
|
||||
void Controller::askUsernameRevoke() {
|
||||
_controls.privacy->setValue(Privacy::Private);
|
||||
auto revokeCallback = base::lambda_guarded(this, [this] {
|
||||
auto revokeCallback = crl::guard(this, [this] {
|
||||
_usernameState = UsernameState::Normal;
|
||||
_controls.privacy->setValue(Privacy::Public);
|
||||
checkUsernameAvailability();
|
||||
@@ -636,7 +644,7 @@ void Controller::revokeInviteLink() {
|
||||
|
||||
void Controller::exportInviteLink(const QString &confirmation) {
|
||||
auto boxPointer = std::make_shared<QPointer<ConfirmBox>>();
|
||||
auto callback = base::lambda_guarded(this, [=] {
|
||||
auto callback = crl::guard(this, [=] {
|
||||
if (auto strong = *boxPointer) {
|
||||
strong->closeBox();
|
||||
}
|
||||
@@ -1177,7 +1185,7 @@ void Controller::save() {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::pushSaveStage(base::lambda_once<void()> &&lambda) {
|
||||
void Controller::pushSaveStage(FnMut<void()> &&lambda) {
|
||||
_saveStagesQueue.push_back(std::move(lambda));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "mainwindow.h"
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
#include "info/profile/info_profile_button.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
@@ -23,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace {
|
||||
|
||||
base::lambda<QString()> ManagePeerTitle(
|
||||
Fn<QString()> ManagePeerTitle(
|
||||
not_null<ChannelData*> channel) {
|
||||
return langFactory(channel->isMegagroup()
|
||||
? lng_manage_group_title
|
||||
@@ -88,9 +89,9 @@ bool HasRecentActions(not_null<ChannelData*> channel) {
|
||||
}
|
||||
|
||||
void ShowRecentActions(
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<Window::Navigation*> navigation,
|
||||
not_null<ChannelData*> channel) {
|
||||
controller->showSection(AdminLog::SectionMemento(channel));
|
||||
navigation->showSection(AdminLog::SectionMemento(channel));
|
||||
}
|
||||
|
||||
bool HasEditInfoBox(not_null<ChannelData*> channel) {
|
||||
@@ -104,7 +105,7 @@ bool HasEditInfoBox(not_null<ChannelData*> channel) {
|
||||
}
|
||||
|
||||
void FillManageBox(
|
||||
not_null<Window::Controller*> controller,
|
||||
not_null<Window::Navigation*> navigation,
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<Ui::VerticalLayout*> content) {
|
||||
using Profile::ParticipantsBoxController;
|
||||
@@ -123,7 +124,7 @@ void FillManageBox(
|
||||
AddButton(
|
||||
content,
|
||||
Lang::Viewer(lng_manage_peer_recent_actions),
|
||||
[=] { ShowRecentActions(controller, channel); },
|
||||
[=] { ShowRecentActions(navigation, channel); },
|
||||
st::infoIconRecentActions);
|
||||
}
|
||||
if (channel->canViewMembers()) {
|
||||
@@ -134,7 +135,7 @@ void FillManageBox(
|
||||
| ToPositiveNumberString(),
|
||||
[=] {
|
||||
ParticipantsBoxController::Start(
|
||||
controller,
|
||||
navigation,
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Members);
|
||||
},
|
||||
@@ -148,7 +149,7 @@ void FillManageBox(
|
||||
| ToPositiveNumberString(),
|
||||
[=] {
|
||||
ParticipantsBoxController::Start(
|
||||
controller,
|
||||
navigation,
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Admins);
|
||||
},
|
||||
@@ -163,7 +164,7 @@ void FillManageBox(
|
||||
| ToPositiveNumberString(),
|
||||
[=] {
|
||||
ParticipantsBoxController::Start(
|
||||
controller,
|
||||
navigation,
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Restricted);
|
||||
},
|
||||
@@ -176,7 +177,7 @@ void FillManageBox(
|
||||
| ToPositiveNumberString(),
|
||||
[=] {
|
||||
ParticipantsBoxController::Start(
|
||||
controller,
|
||||
navigation,
|
||||
channel,
|
||||
ParticipantsBoxController::Role::Kicked);
|
||||
},
|
||||
@@ -218,7 +219,7 @@ void ManagePeerBox::prepare() {
|
||||
|
||||
void ManagePeerBox::setupContent() {
|
||||
auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||
FillManageBox(controller(), _channel, content);
|
||||
FillManageBox(App::wnd()->controller(), _channel, content);
|
||||
widthValue(
|
||||
) | rpl::start_with_next([=](int width) {
|
||||
content->resizeToWidth(width);
|
||||
|
||||
@@ -60,7 +60,7 @@ void RateCallBox::ratingChanged(int value) {
|
||||
Expects(value > 0 && value <= kMaxRating);
|
||||
if (!_rating) {
|
||||
clearButtons();
|
||||
addButton(langFactory(lng_send_button), [this] { onSend(); });
|
||||
addButton(langFactory(lng_send_button), [this] { send(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
}
|
||||
_rating = value;
|
||||
@@ -71,16 +71,20 @@ void RateCallBox::ratingChanged(int value) {
|
||||
}
|
||||
if (value < kMaxRating) {
|
||||
if (!_comment) {
|
||||
_comment.create(this, st::callRatingComment, langFactory(lng_call_rate_comment));
|
||||
_comment.create(
|
||||
this,
|
||||
st::callRatingComment,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_call_rate_comment));
|
||||
_comment->show();
|
||||
_comment->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_comment->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
_comment->setMaxLength(MaxPhotoCaption);
|
||||
_comment->resize(width() - (st::callRatingPadding.left() + st::callRatingPadding.right()), _comment->height());
|
||||
|
||||
updateMaxHeight();
|
||||
connect(_comment, SIGNAL(resized()), this, SLOT(onCommentResized()));
|
||||
connect(_comment, SIGNAL(submitted(bool)), this, SLOT(onSend()));
|
||||
connect(_comment, SIGNAL(cancelled()), this, SLOT(onClose()));
|
||||
connect(_comment, &Ui::InputField::resized, [=] { commentResized(); });
|
||||
connect(_comment, &Ui::InputField::submitted, [=] { send(); });
|
||||
connect(_comment, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
}
|
||||
_comment->setFocusFast();
|
||||
} else if (_comment) {
|
||||
@@ -97,13 +101,14 @@ void RateCallBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void RateCallBox::onCommentResized() {
|
||||
void RateCallBox::commentResized() {
|
||||
updateMaxHeight();
|
||||
update();
|
||||
}
|
||||
|
||||
void RateCallBox::onSend() {
|
||||
void RateCallBox::send() {
|
||||
Expects(_rating > 0 && _rating <= kMaxRating);
|
||||
|
||||
if (_requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11,24 +11,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Ui {
|
||||
class InputArea;
|
||||
class InputField;
|
||||
class FlatLabel;
|
||||
class IconButton;
|
||||
} // namespace Ui
|
||||
|
||||
class RateCallBox : public BoxContent, private MTP::Sender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RateCallBox(QWidget*, uint64 callId, uint64 callAccessHash);
|
||||
|
||||
private slots:
|
||||
void onSend();
|
||||
void onCommentResized();
|
||||
void onClose() {
|
||||
closeBox();
|
||||
}
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
void setInnerFocus() override;
|
||||
@@ -38,13 +29,15 @@ protected:
|
||||
private:
|
||||
void updateMaxHeight();
|
||||
void ratingChanged(int value);
|
||||
void send();
|
||||
void commentResized();
|
||||
|
||||
uint64 _callId = 0;
|
||||
uint64 _callAccessHash = 0;
|
||||
int _rating = 0;
|
||||
|
||||
std::vector<object_ptr<Ui::IconButton>> _stars;
|
||||
object_ptr<Ui::InputArea> _comment = { nullptr };
|
||||
object_ptr<Ui::InputField> _comment = { nullptr };
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
|
||||
@@ -14,23 +14,54 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
ReportBox::ReportBox(QWidget*, PeerData *peer) : _peer(peer)
|
||||
, _reasonGroup(std::make_shared<Ui::RadioenumGroup<Reason>>(Reason::Spam))
|
||||
, _reasonSpam(this, _reasonGroup, Reason::Spam, lang(lng_report_reason_spam), st::defaultBoxCheckbox)
|
||||
, _reasonViolence(this, _reasonGroup, Reason::Violence, lang(lng_report_reason_violence), st::defaultBoxCheckbox)
|
||||
, _reasonPornography(this, _reasonGroup, Reason::Pornography, lang(lng_report_reason_pornography), st::defaultBoxCheckbox)
|
||||
, _reasonOther(this, _reasonGroup, Reason::Other, lang(lng_report_reason_other), st::defaultBoxCheckbox) {
|
||||
ReportBox::ReportBox(QWidget*, not_null<PeerData*> peer)
|
||||
: _peer(peer) {
|
||||
}
|
||||
|
||||
ReportBox::ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids)
|
||||
: _peer(peer)
|
||||
, _ids(std::move(ids)) {
|
||||
}
|
||||
|
||||
void ReportBox::prepare() {
|
||||
setTitle(langFactory(_peer->isUser() ? lng_report_bot_title : (_peer->isMegagroup() ? lng_report_group_title : lng_report_title)));
|
||||
setTitle(langFactory([&] {
|
||||
if (_ids) {
|
||||
return lng_report_message_title;
|
||||
} else if (_peer->isUser()) {
|
||||
return lng_report_bot_title;
|
||||
} else if (_peer->isMegagroup()) {
|
||||
return lng_report_group_title;
|
||||
} else {
|
||||
return lng_report_title;
|
||||
}
|
||||
}()));
|
||||
|
||||
addButton(langFactory(lng_report_button), [this] { onReport(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_report_button), [=] { report(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
_reasonGroup->setChangedCallback([this](Reason value) { reasonChanged(value); });
|
||||
_reasonGroup = std::make_shared<Ui::RadioenumGroup<Reason>>(
|
||||
Reason::Spam);
|
||||
const auto createButton = [&](
|
||||
object_ptr<Ui::Radioenum<Reason>> &button,
|
||||
Reason reason,
|
||||
LangKey key) {
|
||||
button.create(
|
||||
this,
|
||||
_reasonGroup,
|
||||
reason,
|
||||
lang(key),
|
||||
st::defaultBoxCheckbox);
|
||||
};
|
||||
createButton(_reasonSpam, Reason::Spam, lng_report_reason_spam);
|
||||
createButton(_reasonViolence, Reason::Violence, lng_report_reason_violence);
|
||||
createButton(_reasonPornography, Reason::Pornography, lng_report_reason_pornography);
|
||||
createButton(_reasonOther, Reason::Other, lng_report_reason_other);
|
||||
_reasonGroup->setChangedCallback([=](Reason value) {
|
||||
reasonChanged(value);
|
||||
});
|
||||
|
||||
updateMaxHeight();
|
||||
}
|
||||
@@ -51,16 +82,20 @@ void ReportBox::resizeEvent(QResizeEvent *e) {
|
||||
void ReportBox::reasonChanged(Reason reason) {
|
||||
if (reason == Reason::Other) {
|
||||
if (!_reasonOtherText) {
|
||||
_reasonOtherText.create(this, st::profileReportReasonOther, langFactory(lng_report_reason_description));
|
||||
_reasonOtherText.create(
|
||||
this,
|
||||
st::profileReportReasonOther,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
langFactory(lng_report_reason_description));
|
||||
_reasonOtherText->show();
|
||||
_reasonOtherText->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
_reasonOtherText->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
_reasonOtherText->setMaxLength(MaxPhotoCaption);
|
||||
_reasonOtherText->resize(width() - (st::boxPadding.left() + st::boxOptionListPadding.left() + st::boxPadding.right()), _reasonOtherText->height());
|
||||
|
||||
updateMaxHeight();
|
||||
connect(_reasonOtherText, SIGNAL(resized()), this, SLOT(onReasonResized()));
|
||||
connect(_reasonOtherText, SIGNAL(submitted(bool)), this, SLOT(onReport()));
|
||||
connect(_reasonOtherText, SIGNAL(cancelled()), this, SLOT(onClose()));
|
||||
connect(_reasonOtherText, &Ui::InputField::resized, [=] { reasonResized(); });
|
||||
connect(_reasonOtherText, &Ui::InputField::submitted, [=] { report(); });
|
||||
connect(_reasonOtherText, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
}
|
||||
_reasonOtherText->setFocusFast();
|
||||
} else if (_reasonOtherText) {
|
||||
@@ -77,12 +112,12 @@ void ReportBox::setInnerFocus() {
|
||||
}
|
||||
}
|
||||
|
||||
void ReportBox::onReasonResized() {
|
||||
void ReportBox::reasonResized() {
|
||||
updateMaxHeight();
|
||||
update();
|
||||
}
|
||||
|
||||
void ReportBox::onReport() {
|
||||
void ReportBox::report() {
|
||||
if (_requestId) return;
|
||||
|
||||
if (_reasonOtherText && _reasonOtherText->getLastText().trimmed().isEmpty()) {
|
||||
@@ -90,7 +125,7 @@ void ReportBox::onReport() {
|
||||
return;
|
||||
}
|
||||
|
||||
auto getReason = [this]() {
|
||||
const auto reason = [&] {
|
||||
switch (_reasonGroup->value()) {
|
||||
case Reason::Spam: return MTP_inputReportReasonSpam();
|
||||
case Reason::Violence: return MTP_inputReportReasonViolence();
|
||||
@@ -98,17 +133,37 @@ void ReportBox::onReport() {
|
||||
case Reason::Other: return MTP_inputReportReasonOther(MTP_string(_reasonOtherText->getLastText()));
|
||||
}
|
||||
Unexpected("Bad reason group value.");
|
||||
};
|
||||
_requestId = MTP::send(MTPaccount_ReportPeer(_peer->input, getReason()), rpcDone(&ReportBox::reportDone), rpcFail(&ReportBox::reportFail));
|
||||
}();
|
||||
if (_ids) {
|
||||
auto ids = QVector<MTPint>();
|
||||
for (const auto &fullId : *_ids) {
|
||||
ids.push_back(MTP_int(fullId.msg));
|
||||
}
|
||||
_requestId = MTP::send(
|
||||
MTPmessages_Report(
|
||||
_peer->input,
|
||||
MTP_vector<MTPint>(ids),
|
||||
reason),
|
||||
rpcDone(&ReportBox::reportDone),
|
||||
rpcFail(&ReportBox::reportFail));
|
||||
} else {
|
||||
_requestId = MTP::send(
|
||||
MTPaccount_ReportPeer(_peer->input, reason),
|
||||
rpcDone(&ReportBox::reportDone),
|
||||
rpcFail(&ReportBox::reportFail));
|
||||
}
|
||||
}
|
||||
|
||||
void ReportBox::reportDone(const MTPBool &result) {
|
||||
_requestId = 0;
|
||||
Ui::show(Box<InformBox>(lang(lng_report_thanks)));
|
||||
Ui::Toast::Show(lang(lng_report_thanks));
|
||||
closeBox();
|
||||
}
|
||||
|
||||
bool ReportBox::reportFail(const RPCError &error) {
|
||||
if (MTP::isDefaultHandledError(error)) return false;
|
||||
if (MTP::isDefaultHandledError(error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_requestId = 0;
|
||||
if (_reasonOtherText) {
|
||||
|
||||
@@ -14,21 +14,13 @@ template <typename Enum>
|
||||
class RadioenumGroup;
|
||||
template <typename Enum>
|
||||
class Radioenum;
|
||||
class InputArea;
|
||||
class InputField;
|
||||
} // namespace Ui
|
||||
|
||||
class ReportBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ReportBox(QWidget*, PeerData *peer);
|
||||
|
||||
private slots:
|
||||
void onReport();
|
||||
void onReasonResized();
|
||||
void onClose() {
|
||||
closeBox();
|
||||
}
|
||||
ReportBox(QWidget*, not_null<PeerData*> peer);
|
||||
ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -44,19 +36,22 @@ private:
|
||||
Other,
|
||||
};
|
||||
void reasonChanged(Reason reason);
|
||||
void reasonResized();
|
||||
void updateMaxHeight();
|
||||
void report();
|
||||
|
||||
void reportDone(const MTPBool &result);
|
||||
bool reportFail(const RPCError &error);
|
||||
|
||||
PeerData *_peer;
|
||||
not_null<PeerData*> _peer;
|
||||
base::optional<MessageIdsList> _ids;
|
||||
|
||||
std::shared_ptr<Ui::RadioenumGroup<Reason>> _reasonGroup;
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonSpam;
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonViolence;
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonPornography;
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonOther;
|
||||
object_ptr<Ui::InputArea> _reasonOtherText = { nullptr };
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonSpam = { nullptr };
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonViolence = { nullptr };
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonPornography = { nullptr };
|
||||
object_ptr<Ui::Radioenum<Reason>> _reasonOther = { nullptr };
|
||||
object_ptr<Ui::InputField> _reasonOtherText = { nullptr };
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "mainwidget.h"
|
||||
#include "history/history_media_types.h"
|
||||
#include "chat_helpers/message_field.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -124,7 +125,7 @@ public:
|
||||
bool isPointAfter(QPoint position) const;
|
||||
void moveInAlbum(QPoint to);
|
||||
QPoint center() const;
|
||||
void suggestMove(float64 delta, base::lambda<void()> callback);
|
||||
void suggestMove(float64 delta, Fn<void()> callback);
|
||||
void finishAnimations();
|
||||
|
||||
private:
|
||||
@@ -219,6 +220,7 @@ AlbumThumb::AlbumThumb(
|
||||
if (_nameWidth > availableFileWidth) {
|
||||
_name = st::semiboldFont->elided(
|
||||
_name,
|
||||
availableFileWidth,
|
||||
Qt::ElideMiddle);
|
||||
_nameWidth = st::semiboldFont->width(_name);
|
||||
}
|
||||
@@ -518,7 +520,7 @@ QPoint AlbumThumb::center() const {
|
||||
return realGeometry.center();
|
||||
}
|
||||
|
||||
void AlbumThumb::suggestMove(float64 delta, base::lambda<void()> callback) {
|
||||
void AlbumThumb::suggestMove(float64 delta, Fn<void()> callback) {
|
||||
if (_suggestedMove != delta) {
|
||||
_suggestedMoveAnimation.start(
|
||||
std::move(callback),
|
||||
@@ -887,7 +889,7 @@ rpl::producer<int> SingleFilePreview::desiredHeightValue() const {
|
||||
return rpl::single(st::boxPhotoPadding.top() + h + st::msgShadow);
|
||||
}
|
||||
|
||||
base::lambda<QString()> FieldPlaceholder(const Storage::PreparedList &list) {
|
||||
Fn<QString()> FieldPlaceholder(const Storage::PreparedList &list) {
|
||||
return langFactory(list.files.size() > 1
|
||||
? lng_photos_comment
|
||||
: lng_photo_caption);
|
||||
@@ -1318,11 +1320,20 @@ void SendFilesBox::AlbumPreview::mouseReleaseEvent(QMouseEvent *e) {
|
||||
|
||||
SendFilesBox::SendFilesBox(
|
||||
QWidget*,
|
||||
not_null<Window::Controller*> controller,
|
||||
Storage::PreparedList &&list,
|
||||
const TextWithTags &caption,
|
||||
CompressConfirm compressed)
|
||||
: _list(std::move(list))
|
||||
: _controller(controller)
|
||||
, _list(std::move(list))
|
||||
, _compressConfirmInitial(compressed)
|
||||
, _compressConfirm(compressed) {
|
||||
, _compressConfirm(compressed)
|
||||
, _caption(
|
||||
this,
|
||||
st::confirmCaptionArea,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
FieldPlaceholder(_list),
|
||||
caption) {
|
||||
}
|
||||
|
||||
void SendFilesBox::initPreview(rpl::producer<int> desiredPreviewHeight) {
|
||||
@@ -1350,7 +1361,7 @@ void SendFilesBox::prepareSingleFilePreview() {
|
||||
Expects(_list.files.size() == 1);
|
||||
|
||||
const auto &file = _list.files[0];
|
||||
const auto media = SingleMediaPreview::Create(this, controller(), file);
|
||||
const auto media = SingleMediaPreview::Create(this, _controller, file);
|
||||
if (media) {
|
||||
if (!media->canSendAsPhoto()) {
|
||||
_compressConfirm = CompressConfirm::None;
|
||||
@@ -1413,17 +1424,16 @@ void SendFilesBox::setupShadows(
|
||||
}
|
||||
|
||||
void SendFilesBox::prepare() {
|
||||
Expects(controller() != nullptr);
|
||||
|
||||
_send = addButton(langFactory(lng_send_button), [this] { send(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
setupCaption();
|
||||
initSendWay();
|
||||
preparePreview();
|
||||
subscribe(boxClosing, [this] {
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
if (!_confirmed && _cancelledCallback) {
|
||||
_cancelledCallback();
|
||||
}
|
||||
});
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
void SendFilesBox::initSendWay() {
|
||||
@@ -1488,7 +1498,7 @@ void SendFilesBox::preparePreview() {
|
||||
void SendFilesBox::setupControls() {
|
||||
setupTitleText();
|
||||
setupSendWayControls();
|
||||
setupCaption();
|
||||
_caption->setPlaceholder(FieldPlaceholder(_list));
|
||||
}
|
||||
|
||||
void SendFilesBox::setupSendWayControls() {
|
||||
@@ -1545,34 +1555,34 @@ void SendFilesBox::applyAlbumOrder() {
|
||||
}
|
||||
|
||||
void SendFilesBox::setupCaption() {
|
||||
if (_caption) {
|
||||
_caption->setPlaceholder(FieldPlaceholder(_list));
|
||||
return;
|
||||
}
|
||||
|
||||
_caption.create(this, st::confirmCaptionArea, FieldPlaceholder(_list));
|
||||
_caption->setMaxLength(MaxPhotoCaption);
|
||||
_caption->setCtrlEnterSubmit(Ui::CtrlEnterSubmit::Both);
|
||||
connect(_caption, &Ui::InputArea::resized, this, [this] {
|
||||
_caption->setSubmitSettings(Ui::InputField::SubmitSettings::Both);
|
||||
connect(_caption, &Ui::InputField::resized, [=] {
|
||||
captionResized();
|
||||
});
|
||||
connect(_caption, &Ui::InputArea::submitted, this, [this](
|
||||
bool ctrlShiftEnter) {
|
||||
connect(_caption, &Ui::InputField::submitted, [=](
|
||||
Qt::KeyboardModifiers modifiers) {
|
||||
const auto ctrlShiftEnter = modifiers.testFlag(Qt::ShiftModifier)
|
||||
&& (modifiers.testFlag(Qt::ControlModifier)
|
||||
|| modifiers.testFlag(Qt::MetaModifier));
|
||||
send(ctrlShiftEnter);
|
||||
});
|
||||
connect(_caption, &Ui::InputArea::cancelled, this, [this] {
|
||||
closeBox();
|
||||
});
|
||||
_caption->setMimeDataHook([this](
|
||||
connect(_caption, &Ui::InputField::cancelled, [=] { closeBox(); });
|
||||
_caption->setMimeDataHook([=](
|
||||
not_null<const QMimeData*> data,
|
||||
Ui::InputArea::MimeAction action) {
|
||||
if (action == Ui::InputArea::MimeAction::Check) {
|
||||
Ui::InputField::MimeAction action) {
|
||||
if (action == Ui::InputField::MimeAction::Check) {
|
||||
return canAddFiles(data);
|
||||
} else if (action == Ui::InputArea::MimeAction::Insert) {
|
||||
} else if (action == Ui::InputField::MimeAction::Insert) {
|
||||
return addFiles(data);
|
||||
}
|
||||
Unexpected("action in MimeData hook.");
|
||||
});
|
||||
_caption->setInstantReplaces(Ui::InstantReplaces::Default());
|
||||
_caption->setInstantReplacesEnabled(Global::ReplaceEmojiValue());
|
||||
_caption->setMarkdownReplacesEnabled(rpl::single(true));
|
||||
_caption->setEditLinkCallback(
|
||||
DefaultEditLinkCallback(_controller, _caption));
|
||||
}
|
||||
|
||||
void SendFilesBox::captionResized() {
|
||||
@@ -1784,10 +1794,8 @@ void SendFilesBox::send(bool ctrlShiftEnter) {
|
||||
_confirmed = true;
|
||||
if (_confirmedCallback) {
|
||||
auto caption = _caption
|
||||
? TextUtilities::PrepareForSending(
|
||||
_caption->getLastText(),
|
||||
TextUtilities::PrepareTextOption::CheckLinks)
|
||||
: QString();
|
||||
? _caption->getTextWithAppliedMarkdown()
|
||||
: TextWithTags();
|
||||
_confirmedCallback(
|
||||
std::move(_list),
|
||||
way,
|
||||
|
||||
@@ -12,16 +12,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/localimageloader.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
namespace Ui {
|
||||
template <typename Enum>
|
||||
class Radioenum;
|
||||
template <typename Enum>
|
||||
class RadioenumGroup;
|
||||
class RoundButton;
|
||||
class InputArea;
|
||||
class InputField;
|
||||
struct GroupMediaLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window {
|
||||
class Controller;
|
||||
} // namespace Window
|
||||
|
||||
enum class SendFilesWay {
|
||||
Album,
|
||||
Photos,
|
||||
@@ -32,18 +40,20 @@ class SendFilesBox : public BoxContent {
|
||||
public:
|
||||
SendFilesBox(
|
||||
QWidget*,
|
||||
not_null<Window::Controller*> controller,
|
||||
Storage::PreparedList &&list,
|
||||
const TextWithTags &caption,
|
||||
CompressConfirm compressed);
|
||||
|
||||
void setConfirmedCallback(
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
Storage::PreparedList &&list,
|
||||
SendFilesWay way,
|
||||
const QString &caption,
|
||||
TextWithTags &&caption,
|
||||
bool ctrlShiftEnter)> callback) {
|
||||
_confirmedCallback = std::move(callback);
|
||||
}
|
||||
void setCancelledCallback(base::lambda<void()> callback) {
|
||||
void setCancelledCallback(Fn<void()> callback) {
|
||||
_cancelledCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -87,6 +97,8 @@ private:
|
||||
bool canAddUrls(const QList<QUrl> &urls) const;
|
||||
bool addFiles(not_null<const QMimeData*> data);
|
||||
|
||||
not_null<Window::Controller*> _controller;
|
||||
|
||||
QString _titleText;
|
||||
int _titleHeight = 0;
|
||||
|
||||
@@ -95,15 +107,15 @@ private:
|
||||
CompressConfirm _compressConfirmInitial = CompressConfirm::None;
|
||||
CompressConfirm _compressConfirm = CompressConfirm::None;
|
||||
|
||||
base::lambda<void(
|
||||
Fn<void(
|
||||
Storage::PreparedList &&list,
|
||||
SendFilesWay way,
|
||||
const QString &caption,
|
||||
TextWithTags &&caption,
|
||||
bool ctrlShiftEnter)> _confirmedCallback;
|
||||
base::lambda<void()> _cancelledCallback;
|
||||
Fn<void()> _cancelledCallback;
|
||||
bool _confirmed = false;
|
||||
|
||||
object_ptr<Ui::InputArea> _caption = { nullptr };
|
||||
object_ptr<Ui::InputField> _caption = { nullptr };
|
||||
object_ptr<Ui::Radioenum<SendFilesWay>> _sendAlbum = { nullptr };
|
||||
object_ptr<Ui::Radioenum<SendFilesWay>> _sendPhotos = { nullptr };
|
||||
object_ptr<Ui::Radioenum<SendFilesWay>> _sendFiles = { nullptr };
|
||||
|
||||
@@ -28,15 +28,15 @@ void SessionsBox::prepare() {
|
||||
|
||||
setDimensions(st::boxWideWidth, st::sessionsHeight);
|
||||
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this, &_list, &_current), st::sessionsScroll);
|
||||
_inner->resize(width(), st::noContactsHeight);
|
||||
|
||||
connect(_inner, SIGNAL(oneTerminated()), this, SLOT(onOneTerminated()));
|
||||
connect(_inner, SIGNAL(allTerminated()), this, SLOT(onAllTerminated()));
|
||||
connect(_inner, SIGNAL(terminateAll()), this, SLOT(onTerminateAll()));
|
||||
connect(App::wnd(), SIGNAL(checkNewAuthorization()), this, SLOT(onCheckNewAuthorization()));
|
||||
connect(_shortPollTimer, SIGNAL(timeout()), this, SLOT(onShortPollAuthorizations()));
|
||||
|
||||
_inner = setInnerWidget(object_ptr<Inner>(this, &_list, &_current), st::sessionsScroll);
|
||||
_inner->resize(width(), st::noContactsHeight);
|
||||
|
||||
setLoading(true);
|
||||
|
||||
MTP::send(MTPaccount_GetAuthorizations(), rpcDone(&SessionsBox::gotAuthorizations));
|
||||
@@ -71,8 +71,9 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
|
||||
_shortPollRequest = 0;
|
||||
setLoading(false);
|
||||
|
||||
auto availCurrent = st::boxWideWidth - st::sessionPadding.left() - st::sessionTerminateSkip;
|
||||
auto availOther = availCurrent - st::sessionTerminate.iconPosition.x();// -st::sessionTerminate.width - st::sessionTerminateSkip;
|
||||
const auto availCurrent = st::boxWideWidth - st::sessionPadding.left() - st::sessionTerminateSkip;
|
||||
const auto availOther = availCurrent - st::sessionTerminate.iconPosition.x();
|
||||
const auto availInfo = availCurrent - st::sessionTerminate.width;
|
||||
|
||||
_list.clear();
|
||||
if (result.type() != mtpc_account_authorizations) {
|
||||
@@ -165,8 +166,8 @@ void SessionsBox::gotAuthorizations(const MTPaccount_Authorizations &result) {
|
||||
data.nameWidth = st::sessionNameFont->width(data.name);
|
||||
}
|
||||
data.infoWidth = st::sessionInfoFont->width(data.info);
|
||||
if (data.infoWidth > availOther) {
|
||||
data.info = st::sessionInfoFont->elided(data.info, availOther);
|
||||
if (data.infoWidth > availInfo) {
|
||||
data.info = st::sessionInfoFont->elided(data.info, availInfo);
|
||||
data.infoWidth = st::sessionInfoFont->width(data.info);
|
||||
}
|
||||
data.ipWidth = st::sessionInfoFont->width(data.ip);
|
||||
@@ -243,8 +244,8 @@ void SessionsBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.translate(0, st::sessionCurrentPadding.top());
|
||||
if (r.y() <= st::sessionCurrentHeight) {
|
||||
p.translate(0, st::sessionCurrentPadding.top());
|
||||
p.setFont(st::sessionNameFont);
|
||||
p.setPen(st::sessionNameFg);
|
||||
p.drawTextLeft(x, st::sessionPadding.top(), w, _current->name, _current->nameWidth);
|
||||
@@ -297,7 +298,7 @@ void SessionsBox::Inner::onTerminate() {
|
||||
for (auto i = _terminateButtons.begin(), e = _terminateButtons.end(); i != e; ++i) {
|
||||
if (i.value()->isOver()) {
|
||||
if (_terminateBox) _terminateBox->deleteLater();
|
||||
_terminateBox = Ui::show(Box<ConfirmBox>(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button), st::attentionBoxButton, base::lambda_guarded(this, [this, terminating = i.key()] {
|
||||
_terminateBox = Ui::show(Box<ConfirmBox>(lang(lng_settings_reset_one_sure), lang(lng_settings_reset_button), st::attentionBoxButton, crl::guard(this, [this, terminating = i.key()] {
|
||||
if (_terminateBox) {
|
||||
_terminateBox->closeBox();
|
||||
_terminateBox = nullptr;
|
||||
@@ -315,7 +316,7 @@ void SessionsBox::Inner::onTerminate() {
|
||||
|
||||
void SessionsBox::Inner::onTerminateAll() {
|
||||
if (_terminateBox) _terminateBox->deleteLater();
|
||||
_terminateBox = Ui::show(Box<ConfirmBox>(lang(lng_settings_reset_sure), lang(lng_settings_reset_button), st::attentionBoxButton, base::lambda_guarded(this, [this] {
|
||||
_terminateBox = Ui::show(Box<ConfirmBox>(lang(lng_settings_reset_sure), lang(lng_settings_reset_button), st::attentionBoxButton, crl::guard(this, [this] {
|
||||
if (_terminateBox) {
|
||||
_terminateBox->closeBox();
|
||||
_terminateBox = nullptr;
|
||||
|
||||
@@ -61,7 +61,14 @@ void ShareBox::prepare() {
|
||||
}
|
||||
});
|
||||
_select->setResizedCallback([this] { updateScrollSkips(); });
|
||||
_select->setSubmittedCallback([this](bool) { _inner->onSelectActive(); });
|
||||
_select->setSubmittedCallback([this](Qt::KeyboardModifiers modifiers) {
|
||||
if (modifiers.testFlag(Qt::ControlModifier)
|
||||
|| modifiers.testFlag(Qt::MetaModifier)) {
|
||||
onSubmit();
|
||||
} else {
|
||||
_inner->onSelectActive();
|
||||
}
|
||||
});
|
||||
connect(_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername()));
|
||||
_inner->setPeerSelectedChangedCallback([this](PeerData *peer, bool checked) {
|
||||
onPeerSelectedChanged(peer, checked);
|
||||
@@ -544,7 +551,7 @@ void ShareBox::Inner::paintChat(
|
||||
chat->name.drawLeftElided(p, x + nameLeft, y + nameTop, nameWidth, outerWidth, 2, style::al_top, 0, -1, 0, true);
|
||||
}
|
||||
|
||||
ShareBox::Inner::Chat::Chat(PeerData *peer, base::lambda<void()> updateCallback)
|
||||
ShareBox::Inner::Chat::Chat(PeerData *peer, Fn<void()> updateCallback)
|
||||
: peer(peer)
|
||||
, checkbox(st::sharePhotoCheckbox, updateCallback, PaintUserpicCallback(peer, true))
|
||||
, name(st::sharePhotoCheckbox.imageRadius * 2) {
|
||||
@@ -692,7 +699,7 @@ void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) {
|
||||
}
|
||||
}
|
||||
|
||||
void ShareBox::Inner::setPeerSelectedChangedCallback(base::lambda<void(PeerData *peer, bool selected)> callback) {
|
||||
void ShareBox::Inner::setPeerSelectedChangedCallback(Fn<void(PeerData *peer, bool selected)> callback) {
|
||||
_peerSelectedChangedCallback = std::move(callback);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ class ShareBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using CopyCallback = base::lambda<void()>;
|
||||
using SubmitCallback = base::lambda<void(const QVector<PeerData*> &)>;
|
||||
using FilterCallback = base::lambda<bool(PeerData*)>;
|
||||
using CopyCallback = Fn<void()>;
|
||||
using SubmitCallback = Fn<void(const QVector<PeerData*> &)>;
|
||||
using FilterCallback = Fn<bool(PeerData*)>;
|
||||
ShareBox(QWidget*, CopyCallback &©Callback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback);
|
||||
|
||||
protected:
|
||||
@@ -103,7 +103,7 @@ class ShareBox::Inner : public TWidget, public RPCSender, private base::Subscrib
|
||||
public:
|
||||
Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallback);
|
||||
|
||||
void setPeerSelectedChangedCallback(base::lambda<void(PeerData *peer, bool selected)> callback);
|
||||
void setPeerSelectedChangedCallback(Fn<void(PeerData *peer, bool selected)> callback);
|
||||
void peerUnselected(not_null<PeerData*> peer);
|
||||
|
||||
QVector<PeerData*> selected() const;
|
||||
@@ -148,7 +148,7 @@ private:
|
||||
int displayedChatsCount() const;
|
||||
|
||||
struct Chat {
|
||||
Chat(PeerData *peer, base::lambda<void()> updateCallback);
|
||||
Chat(PeerData *peer, Fn<void()> updateCallback);
|
||||
|
||||
PeerData *peer;
|
||||
Ui::RoundImageCheckbox checkbox;
|
||||
@@ -201,7 +201,7 @@ private:
|
||||
using SelectedChats = OrderedSet<PeerData*>;
|
||||
SelectedChats _selected;
|
||||
|
||||
base::lambda<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
|
||||
Fn<void(PeerData *peer, bool selected)> _peerSelectedChangedCallback;
|
||||
|
||||
ChatData *data(Dialogs::Row *row);
|
||||
|
||||
|
||||
@@ -381,7 +381,7 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
p.setOpacity(1);
|
||||
|
||||
}
|
||||
bool goodThumb = !doc->thumb->isNull() && ((doc->thumb->width() >= 128) || (doc->thumb->height() >= 128));
|
||||
const auto goodThumb = doc->hasGoodStickerThumb();
|
||||
if (goodThumb) {
|
||||
doc->thumb->load();
|
||||
} else {
|
||||
@@ -389,7 +389,9 @@ void StickerSetBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
doc->automaticLoad(0);
|
||||
}
|
||||
if (doc->sticker()->img->isNull() && doc->loaded(DocumentData::FilePathResolveChecked)) {
|
||||
doc->sticker()->img = doc->data().isEmpty() ? ImagePtr(doc->filepath()) : ImagePtr(doc->data());
|
||||
doc->sticker()->img = doc->data().isEmpty()
|
||||
? ImagePtr(doc->filepath())
|
||||
: ImagePtr(doc->data());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +431,7 @@ bool StickerSetBox::Inner::official() const {
|
||||
return _loaded && _setShortName.isEmpty();
|
||||
}
|
||||
|
||||
base::lambda<TextWithEntities()> StickerSetBox::Inner::title() const {
|
||||
Fn<TextWithEntities()> StickerSetBox::Inner::title() const {
|
||||
auto text = TextWithEntities { _setTitle };
|
||||
if (_loaded) {
|
||||
if (_pack.isEmpty()) {
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
bool loaded() const;
|
||||
bool notInstalled() const;
|
||||
bool official() const;
|
||||
base::lambda<TextWithEntities()> title() const;
|
||||
Fn<TextWithEntities()> title() const;
|
||||
QString shortName() const;
|
||||
|
||||
void install();
|
||||
|
||||
@@ -274,7 +274,9 @@ void StickersBox::prepare() {
|
||||
if (_installed.widget()) {
|
||||
connect(_installed.widget(), SIGNAL(draggingScrollDelta(int)), this, SLOT(onDraggingScrollDelta(int)));
|
||||
if (!_megagroupSet) {
|
||||
subscribe(boxClosing, [this] { saveChanges(); });
|
||||
boxClosing() | rpl::start_with_next([=] {
|
||||
saveChanges();
|
||||
}, lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,14 +612,14 @@ StickersBox::Inner::Inner(QWidget *parent, not_null<ChannelData*> megagroup) : T
|
||||
connect(
|
||||
_megagroupSetField,
|
||||
&Ui::MaskedInputField::changed,
|
||||
[this] {
|
||||
[=] {
|
||||
_megagroupSetAddressChangedTimer.callOnce(
|
||||
kHandleMegagroupSetAddressChangeTimeout);
|
||||
});
|
||||
connect(
|
||||
_megagroupSetField,
|
||||
&Ui::MaskedInputField::submitted,
|
||||
[this] {
|
||||
[=] {
|
||||
_megagroupSetAddressChangedTimer.cancel();
|
||||
handleMegagroupSetAddressChange();
|
||||
});
|
||||
|
||||
@@ -154,10 +154,10 @@ public:
|
||||
void setFullOrder(const Stickers::Order &order);
|
||||
void setRemovedSets(const Stickers::Order &removed);
|
||||
|
||||
void setInstallSetCallback(base::lambda<void(uint64 setId)> callback) {
|
||||
void setInstallSetCallback(Fn<void(uint64 setId)> callback) {
|
||||
_installSetCallback = std::move(callback);
|
||||
}
|
||||
void setLoadMoreCallback(base::lambda<void()> callback) {
|
||||
void setLoadMoreCallback(Fn<void()> callback) {
|
||||
_loadMoreCallback = std::move(callback);
|
||||
}
|
||||
|
||||
@@ -280,8 +280,8 @@ private:
|
||||
anim::value _aboveShadowFadeOpacity;
|
||||
BasicAnimation _a_shifting;
|
||||
|
||||
base::lambda<void(uint64 setId)> _installSetCallback;
|
||||
base::lambda<void()> _loadMoreCallback;
|
||||
Fn<void(uint64 setId)> _installSetCallback;
|
||||
Fn<void()> _loadMoreCallback;
|
||||
|
||||
int _visibleTop = 0;
|
||||
int _visibleBottom = 0;
|
||||
|
||||
@@ -35,18 +35,18 @@ void UsernameBox::prepare() {
|
||||
|
||||
setTitle(langFactory(lng_username_title));
|
||||
|
||||
addButton(langFactory(lng_settings_save), [this] { onSave(); });
|
||||
addButton(langFactory(lng_cancel), [this] { closeBox(); });
|
||||
addButton(langFactory(lng_settings_save), [=] { save(); });
|
||||
addButton(langFactory(lng_cancel), [=] { closeBox(); });
|
||||
|
||||
connect(_username, SIGNAL(changed()), this, SLOT(onChanged()));
|
||||
connect(_username, SIGNAL(submitted(bool)), this, SLOT(onSave()));
|
||||
connect(_link, SIGNAL(clicked()), this, SLOT(onLinkClick()));
|
||||
connect(_username, &Ui::MaskedInputField::changed, [=] { changed(); });
|
||||
connect(_username, &Ui::MaskedInputField::submitted, [=] { save(); });
|
||||
_link->addClickHandler([=] { linkClick(); });
|
||||
|
||||
_about.setRichText(st::usernameTextStyle, lang(lng_username_about));
|
||||
setDimensions(st::boxWidth, st::usernamePadding.top() + _username->height() + st::usernameSkip + _about.countHeight(st::boxWidth - st::usernamePadding.left()) + 3 * st::usernameTextStyle.lineHeight + st::usernamePadding.bottom());
|
||||
|
||||
_checkTimer->setSingleShot(true);
|
||||
connect(_checkTimer, SIGNAL(timeout()), this, SLOT(onCheck()));
|
||||
connect(_checkTimer, &QTimer::timeout, [=] { check(); });
|
||||
|
||||
updateLinkText();
|
||||
}
|
||||
@@ -96,14 +96,14 @@ void UsernameBox::resizeEvent(QResizeEvent *e) {
|
||||
_link->moveToLeft(st::usernamePadding.left(), linky + st::usernameTextStyle.lineHeight + ((st::usernameTextStyle.lineHeight - st::boxTextFont->height) / 2));
|
||||
}
|
||||
|
||||
void UsernameBox::onSave() {
|
||||
void UsernameBox::save() {
|
||||
if (_saveRequestId) return;
|
||||
|
||||
_sentUsername = getName();
|
||||
_saveRequestId = MTP::send(MTPaccount_UpdateUsername(MTP_string(_sentUsername)), rpcDone(&UsernameBox::onUpdateDone), rpcFail(&UsernameBox::onUpdateFail));
|
||||
}
|
||||
|
||||
void UsernameBox::onCheck() {
|
||||
void UsernameBox::check() {
|
||||
if (_checkRequestId) {
|
||||
MTP::cancel(_checkRequestId);
|
||||
}
|
||||
@@ -118,7 +118,7 @@ void UsernameBox::onCheck() {
|
||||
}
|
||||
}
|
||||
|
||||
void UsernameBox::onChanged() {
|
||||
void UsernameBox::changed() {
|
||||
updateLinkText();
|
||||
QString name = getName();
|
||||
if (name.isEmpty()) {
|
||||
@@ -156,7 +156,7 @@ void UsernameBox::onChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void UsernameBox::onLinkClick() {
|
||||
void UsernameBox::linkClick() {
|
||||
Application::clipboard()->setText(Messenger::Instance().createInternalLinkFull(getName()));
|
||||
Ui::Toast::Show(lang(lng_username_copied));
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ class LinkButton;
|
||||
} // namespace Ui
|
||||
|
||||
class UsernameBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UsernameBox(QWidget*);
|
||||
|
||||
@@ -27,14 +25,6 @@ protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void onSave();
|
||||
|
||||
void onCheck();
|
||||
void onChanged();
|
||||
|
||||
void onLinkClick();
|
||||
|
||||
private:
|
||||
void onUpdateDone(const MTPUser &result);
|
||||
bool onUpdateFail(const RPCError &error);
|
||||
@@ -42,6 +32,13 @@ private:
|
||||
void onCheckDone(const MTPBool &result);
|
||||
bool onCheckFail(const RPCError &error);
|
||||
|
||||
void save();
|
||||
|
||||
void check();
|
||||
void changed();
|
||||
|
||||
void linkClick();
|
||||
|
||||
QString getName() const;
|
||||
void updateLinkText();
|
||||
|
||||
|
||||
@@ -10,8 +10,17 @@ using "basic.style";
|
||||
using "ui/widgets/widgets.style";
|
||||
using "window/window.style";
|
||||
|
||||
CallSignalBars {
|
||||
width: pixels;
|
||||
radius: pixels;
|
||||
skip: pixels;
|
||||
color: color;
|
||||
inactiveOpacity: double;
|
||||
}
|
||||
|
||||
callWidth: 300px;
|
||||
callHeight: 470px;
|
||||
callRadius: 6px;
|
||||
callShadow: Shadow {
|
||||
left: icon {{ "call_shadow_left", windowShadowFg }};
|
||||
topLeft: icon {{ "call_shadow_top_left", windowShadowFg }};
|
||||
@@ -189,3 +198,16 @@ callDebugLabel: FlatLabel(defaultFlatLabel) {
|
||||
margin: callDebugPadding;
|
||||
}
|
||||
callPanelDuration: 150;
|
||||
|
||||
callPanelSignalBars: CallSignalBars {
|
||||
width: 3px;
|
||||
radius: 1px;
|
||||
skip: 1px;
|
||||
color: callNameFg;
|
||||
inactiveOpacity: 0.5;
|
||||
}
|
||||
callBarSignalBars: CallSignalBars(callPanelSignalBars) {
|
||||
color: callBarFg;
|
||||
}
|
||||
callSignalMargin: 8px;
|
||||
callSignalPadding: 4px;
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
}
|
||||
void addItem(not_null<HistoryItem*> item) {
|
||||
Expects(canAddItem(item));
|
||||
|
||||
_items.push_back(item);
|
||||
ranges::sort(_items, [](not_null<HistoryItem*> a, auto b) {
|
||||
return (a->id > b->id);
|
||||
@@ -78,7 +79,7 @@ public:
|
||||
int availableWidth,
|
||||
int outerWidth,
|
||||
bool selected) override;
|
||||
void addActionRipple(QPoint point, base::lambda<void()> updateCallback) override;
|
||||
void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
|
||||
void stopLastActionRipple() override;
|
||||
|
||||
int nameIconWidth() const override {
|
||||
@@ -191,7 +192,7 @@ BoxController::Row::Type BoxController::Row::ComputeType(
|
||||
return Type::In;
|
||||
}
|
||||
|
||||
void BoxController::Row::addActionRipple(QPoint point, base::lambda<void()> updateCallback) {
|
||||
void BoxController::Row::addActionRipple(QPoint point, Fn<void()> updateCallback) {
|
||||
if (!_actionRipple) {
|
||||
auto mask = Ui::RippleAnimation::ellipseMask(QSize(st::callReDial.rippleAreaSize, st::callReDial.rippleAreaSize));
|
||||
_actionRipple = std::make_unique<Ui::RippleAnimation>(st::callReDial.ripple, std::move(mask), std::move(updateCallback));
|
||||
|
||||
@@ -35,8 +35,9 @@ namespace Calls {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMinLayer = 65;
|
||||
constexpr auto kMaxLayer = 65; // MTP::CurrentLayer?
|
||||
constexpr auto kMaxLayer = 75;
|
||||
constexpr auto kHangupTimeoutMs = 5000;
|
||||
constexpr auto kSha256Size = 32;
|
||||
|
||||
using tgvoip::Endpoint;
|
||||
|
||||
@@ -57,13 +58,14 @@ void ConvertEndpoint(
|
||||
(uint16_t)mtc.vport.v,
|
||||
ipv4,
|
||||
ipv6,
|
||||
EP_TYPE_UDP_RELAY,
|
||||
tgvoip::Endpoint::TYPE_UDP_RELAY,
|
||||
(unsigned char*)mtc.vpeer_tag.v.data()));
|
||||
}
|
||||
|
||||
constexpr auto kFingerprintDataSize = 256;
|
||||
uint64 ComputeFingerprint(
|
||||
const std::array<gsl::byte, kFingerprintDataSize> &authKey) {
|
||||
uint64 ComputeFingerprint(bytes::const_span authKey) {
|
||||
Expects(authKey.size() == kFingerprintDataSize);
|
||||
|
||||
auto hash = openssl::Sha1(authKey);
|
||||
return (gsl::to_integer<uint64>(hash[19]) << 56)
|
||||
| (gsl::to_integer<uint64>(hash[18]) << 48)
|
||||
@@ -77,6 +79,46 @@ uint64 ComputeFingerprint(
|
||||
|
||||
} // namespace
|
||||
|
||||
void Call::ControllerPointer::create() {
|
||||
Expects(_data == nullptr);
|
||||
|
||||
_data = std::make_unique<tgvoip::VoIPController>();
|
||||
}
|
||||
|
||||
void Call::ControllerPointer::reset() {
|
||||
if (const auto controller = base::take(_data)) {
|
||||
controller->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool Call::ControllerPointer::empty() const {
|
||||
return (_data == nullptr);
|
||||
}
|
||||
|
||||
bool Call::ControllerPointer::operator==(std::nullptr_t) const {
|
||||
return empty();
|
||||
}
|
||||
|
||||
Call::ControllerPointer::operator bool() const {
|
||||
return !empty();
|
||||
}
|
||||
|
||||
tgvoip::VoIPController *Call::ControllerPointer::operator->() const {
|
||||
Expects(!empty());
|
||||
|
||||
return _data.get();
|
||||
}
|
||||
|
||||
tgvoip::VoIPController &Call::ControllerPointer::operator*() const {
|
||||
Expects(!empty());
|
||||
|
||||
return *_data;
|
||||
}
|
||||
|
||||
Call::ControllerPointer::~ControllerPointer() {
|
||||
reset();
|
||||
}
|
||||
|
||||
Call::Call(
|
||||
not_null<Delegate*> delegate,
|
||||
not_null<UserData*> user,
|
||||
@@ -93,7 +135,7 @@ Call::Call(
|
||||
}
|
||||
}
|
||||
|
||||
void Call::generateModExpFirst(base::const_byte_span randomSeed) {
|
||||
void Call::generateModExpFirst(bytes::const_span randomSeed) {
|
||||
auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed);
|
||||
if (first.modexp.empty()) {
|
||||
LOG(("Call Error: Could not compute mod-exp first."));
|
||||
@@ -101,7 +143,7 @@ void Call::generateModExpFirst(base::const_byte_span randomSeed) {
|
||||
return;
|
||||
}
|
||||
|
||||
_randomPower = first.randomPower;
|
||||
_randomPower = std::move(first.randomPower);
|
||||
if (_type == Type::Incoming) {
|
||||
_gb = std::move(first.modexp);
|
||||
} else {
|
||||
@@ -117,7 +159,7 @@ bool Call::isIncomingWaiting() const {
|
||||
return (_state == State::Starting) || (_state == State::WaitingIncoming);
|
||||
}
|
||||
|
||||
void Call::start(base::const_byte_span random) {
|
||||
void Call::start(bytes::const_span random) {
|
||||
// Save config here, because it is possible that it changes between
|
||||
// different usages inside the same call.
|
||||
_dhConfig = _delegate->getDhConfig();
|
||||
@@ -139,6 +181,7 @@ void Call::start(base::const_byte_span random) {
|
||||
void Call::startOutgoing() {
|
||||
Expects(_type == Type::Outgoing);
|
||||
Expects(_state == State::Requesting);
|
||||
Expects(_gaHash.size() == kSha256Size);
|
||||
|
||||
request(MTPphone_RequestCall(
|
||||
_user->inputUser,
|
||||
@@ -271,12 +314,8 @@ void Call::redial() {
|
||||
}
|
||||
|
||||
QString Call::getDebugLog() const {
|
||||
constexpr auto kDebugLimit = 4096;
|
||||
auto bytes = base::byte_vector(kDebugLimit, gsl::byte {});
|
||||
_controller->GetDebugString(reinterpret_cast<char*>(bytes.data()), bytes.size());
|
||||
auto end = std::find(bytes.begin(), bytes.end(), gsl::byte {});
|
||||
auto size = (end - bytes.begin());
|
||||
return QString::fromUtf8(reinterpret_cast<const char*>(bytes.data()), size);
|
||||
const auto debug = _controller->GetDebugString();
|
||||
return QString::fromUtf8(debug.data(), debug.size());
|
||||
}
|
||||
|
||||
void Call::startWaitingTrack() {
|
||||
@@ -302,12 +341,13 @@ bool Call::isKeyShaForFingerprintReady() const {
|
||||
return (_keyFingerprint != 0);
|
||||
}
|
||||
|
||||
base::byte_array<Call::kSha256Size> Call::getKeyShaForFingerprint() const {
|
||||
bytes::vector Call::getKeyShaForFingerprint() const {
|
||||
Expects(isKeyShaForFingerprintReady());
|
||||
Expects(!_ga.empty());
|
||||
auto encryptedChatAuthKey = base::byte_vector(_authKey.size() + _ga.size(), gsl::byte {});
|
||||
base::copy_bytes(gsl::make_span(encryptedChatAuthKey).subspan(0, _authKey.size()), _authKey);
|
||||
base::copy_bytes(gsl::make_span(encryptedChatAuthKey).subspan(_authKey.size(), _ga.size()), _ga);
|
||||
|
||||
auto encryptedChatAuthKey = bytes::vector(_authKey.size() + _ga.size(), gsl::byte {});
|
||||
bytes::copy(gsl::make_span(encryptedChatAuthKey).subspan(0, _authKey.size()), _authKey);
|
||||
bytes::copy(gsl::make_span(encryptedChatAuthKey).subspan(_authKey.size(), _ga.size()), _ga);
|
||||
return openssl::Sha256(encryptedChatAuthKey);
|
||||
}
|
||||
|
||||
@@ -327,13 +367,13 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
||||
}
|
||||
_id = data.vid.v;
|
||||
_accessHash = data.vaccess_hash.v;
|
||||
auto gaHashBytes = bytesFromMTP(data.vg_a_hash);
|
||||
if (gaHashBytes.size() != _gaHash.size()) {
|
||||
LOG(("Call Error: Wrong g_a_hash size %1, expected %2.").arg(gaHashBytes.size()).arg(_gaHash.size()));
|
||||
auto gaHashBytes = bytes::make_span(data.vg_a_hash.v);
|
||||
if (gaHashBytes.size() != kSha256Size) {
|
||||
LOG(("Call Error: Wrong g_a_hash size %1, expected %2.").arg(gaHashBytes.size()).arg(kSha256Size));
|
||||
finish(FinishType::Failed);
|
||||
return true;
|
||||
}
|
||||
base::copy_bytes(gsl::make_span(_gaHash), gaHashBytes);
|
||||
_gaHash = bytes::make_vector(gaHashBytes);
|
||||
} return true;
|
||||
|
||||
case mtpc_phoneCallEmpty: {
|
||||
@@ -413,7 +453,7 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
||||
void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
|
||||
Expects(_type == Type::Outgoing);
|
||||
|
||||
auto firstBytes = bytesFromMTP(call.vg_b);
|
||||
auto firstBytes = bytes::make_span(call.vg_b.v);
|
||||
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p);
|
||||
if (computedAuthKey.empty()) {
|
||||
LOG(("Call Error: Could not compute mod-exp final."));
|
||||
@@ -453,13 +493,13 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
|
||||
void Call::startConfirmedCall(const MTPDphoneCall &call) {
|
||||
Expects(_type == Type::Incoming);
|
||||
|
||||
auto firstBytes = bytesFromMTP(call.vg_a_or_b);
|
||||
auto firstBytes = bytes::make_span(call.vg_a_or_b.v);
|
||||
if (_gaHash != openssl::Sha256(firstBytes)) {
|
||||
LOG(("Call Error: Wrong g_a hash received."));
|
||||
finish(FinishType::Failed);
|
||||
return;
|
||||
}
|
||||
_ga = base::byte_vector(firstBytes.begin(), firstBytes.end());
|
||||
_ga = bytes::vector(firstBytes.begin(), firstBytes.end());
|
||||
|
||||
auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p);
|
||||
if (computedAuthKey.empty()) {
|
||||
@@ -480,8 +520,8 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
return;
|
||||
}
|
||||
|
||||
voip_config_t config = { 0 };
|
||||
config.data_saving = DATA_SAVING_NEVER;
|
||||
tgvoip::VoIPController::Config config;
|
||||
config.dataSaving = tgvoip::DATA_SAVING_NEVER;
|
||||
#ifdef Q_OS_MAC
|
||||
config.enableAEC = (QSysInfo::macVersion() < QSysInfo::MV_10_7);
|
||||
#else // Q_OS_MAC
|
||||
@@ -489,38 +529,51 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
#endif // Q_OS_MAC
|
||||
config.enableNS = true;
|
||||
config.enableAGC = true;
|
||||
config.init_timeout = Global::CallConnectTimeoutMs() / 1000;
|
||||
config.recv_timeout = Global::CallPacketTimeoutMs() / 1000;
|
||||
if (cDebug()) {
|
||||
config.initTimeout = Global::CallConnectTimeoutMs() / 1000;
|
||||
config.recvTimeout = Global::CallPacketTimeoutMs() / 1000;
|
||||
if (Logs::DebugEnabled()) {
|
||||
auto callLogFolder = cWorkingDir() + qsl("DebugLogs");
|
||||
auto callLogPath = callLogFolder + qsl("/last_call_log.txt");
|
||||
auto callLogNative = QFile::encodeName(QDir::toNativeSeparators(callLogPath));
|
||||
auto callLogBytesSrc = gsl::as_bytes(gsl::make_span(callLogNative));
|
||||
auto callLogBytesDst = gsl::as_writeable_bytes(gsl::make_span(config.logFilePath));
|
||||
auto callLogBytesSrc = bytes::make_span(callLogNative);
|
||||
auto callLogBytesDst = bytes::make_span(config.logFilePath);
|
||||
if (callLogBytesSrc.size() + 1 <= callLogBytesDst.size()) { // +1 - zero-terminator
|
||||
QFile(callLogPath).remove();
|
||||
QDir().mkpath(callLogFolder);
|
||||
base::copy_bytes(callLogBytesDst, callLogBytesSrc);
|
||||
bytes::copy(callLogBytesDst, callLogBytesSrc);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Endpoint> endpoints;
|
||||
const auto &protocol = call.vprotocol.c_phoneCallProtocol();
|
||||
auto endpoints = std::vector<Endpoint>();
|
||||
ConvertEndpoint(endpoints, call.vconnection.c_phoneConnection());
|
||||
for (int i = 0; i < call.valternative_connections.v.length(); i++) {
|
||||
ConvertEndpoint(endpoints, call.valternative_connections.v[i].c_phoneConnection());
|
||||
}
|
||||
|
||||
_controller = std::make_unique<tgvoip::VoIPController>();
|
||||
auto callbacks = tgvoip::VoIPController::Callbacks();
|
||||
callbacks.connectionStateChanged = [](
|
||||
tgvoip::VoIPController *controller,
|
||||
int state) {
|
||||
const auto call = static_cast<Call*>(controller->implData);
|
||||
call->handleControllerStateChange(controller, state);
|
||||
};
|
||||
callbacks.signalBarCountChanged = [](
|
||||
tgvoip::VoIPController *controller,
|
||||
int count) {
|
||||
const auto call = static_cast<Call*>(controller->implData);
|
||||
call->handleControllerBarCountChange(controller, count);
|
||||
};
|
||||
|
||||
_controller.create();
|
||||
if (_mute) {
|
||||
_controller->SetMicMute(_mute);
|
||||
}
|
||||
_controller->implData = static_cast<void*>(this);
|
||||
_controller->SetRemoteEndpoints(endpoints, true);
|
||||
_controller->SetConfig(&config);
|
||||
_controller->SetRemoteEndpoints(endpoints, true, protocol.vmax_layer.v);
|
||||
_controller->SetConfig(config);
|
||||
_controller->SetEncryptionKey(reinterpret_cast<char*>(_authKey.data()), (_type == Type::Outgoing));
|
||||
_controller->SetStateCallback([](tgvoip::VoIPController *controller, int state) {
|
||||
static_cast<Call*>(controller->implData)->handleControllerStateChange(controller, state);
|
||||
});
|
||||
_controller->SetCallbacks(callbacks);
|
||||
if (Global::UseProxy() && Global::UseProxyForCalls()) {
|
||||
const auto proxy = Global::SelectedProxy();
|
||||
if (proxy.supportsCalls()) {
|
||||
@@ -537,28 +590,31 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
_controller->Connect();
|
||||
}
|
||||
|
||||
void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int state) {
|
||||
void Call::handleControllerStateChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int state) {
|
||||
// NB! Can be called from an arbitrary thread!
|
||||
// Expects(controller == _controller.get()); This can be called from ~VoIPController()!
|
||||
// This can be called from ~VoIPController()!
|
||||
// Expects(controller == _controller.get());
|
||||
Expects(controller->implData == static_cast<void*>(this));
|
||||
|
||||
switch (state) {
|
||||
case STATE_WAIT_INIT: {
|
||||
case tgvoip::STATE_WAIT_INIT: {
|
||||
DEBUG_LOG(("Call Info: State changed to WaitingInit."));
|
||||
setStateQueued(State::WaitingInit);
|
||||
} break;
|
||||
|
||||
case STATE_WAIT_INIT_ACK: {
|
||||
case tgvoip::STATE_WAIT_INIT_ACK: {
|
||||
DEBUG_LOG(("Call Info: State changed to WaitingInitAck."));
|
||||
setStateQueued(State::WaitingInitAck);
|
||||
} break;
|
||||
|
||||
case STATE_ESTABLISHED: {
|
||||
case tgvoip::STATE_ESTABLISHED: {
|
||||
DEBUG_LOG(("Call Info: State changed to Established."));
|
||||
setStateQueued(State::Established);
|
||||
} break;
|
||||
|
||||
case STATE_FAILED: {
|
||||
case tgvoip::STATE_FAILED: {
|
||||
auto error = controller->GetLastError();
|
||||
LOG(("Call Info: State changed to Failed, error: %1.").arg(error));
|
||||
setFailedQueued(error);
|
||||
@@ -568,6 +624,26 @@ void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int s
|
||||
}
|
||||
}
|
||||
|
||||
void Call::handleControllerBarCountChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int count) {
|
||||
// NB! Can be called from an arbitrary thread!
|
||||
// This can be called from ~VoIPController()!
|
||||
// Expects(controller == _controller.get());
|
||||
Expects(controller->implData == static_cast<void*>(this));
|
||||
|
||||
InvokeQueued(this, [=] {
|
||||
setSignalBarCount(count);
|
||||
});
|
||||
}
|
||||
|
||||
void Call::setSignalBarCount(int count) {
|
||||
if (_signalBarCount != count) {
|
||||
_signalBarCount = count;
|
||||
_signalBarCountChanged.notify(count);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Call::checkCallCommonFields(const T &call) {
|
||||
auto checkFailed = [this] {
|
||||
@@ -661,6 +737,9 @@ void Call::setState(State state) {
|
||||
|
||||
void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||
Expects(type != FinishType::None);
|
||||
|
||||
setSignalBarCount(kSignalBarFinished);
|
||||
|
||||
auto finalState = (type == FinishType::Ended) ? State::Ended : State::Failed;
|
||||
auto hangupState = (type == FinishType::Ended) ? State::HangingUp : State::FailedHangingUp;
|
||||
if (_state == State::Requesting) {
|
||||
@@ -695,11 +774,15 @@ void Call::finish(FinishType type, const MTPPhoneCallDiscardReason &reason) {
|
||||
}
|
||||
|
||||
void Call::setStateQueued(State state) {
|
||||
InvokeQueued(this, [this, state] { setState(state); });
|
||||
InvokeQueued(this, [=] {
|
||||
setState(state);
|
||||
});
|
||||
}
|
||||
|
||||
void Call::setFailedQueued(int error) {
|
||||
InvokeQueued(this, [this, error] { handleControllerError(error); });
|
||||
InvokeQueued(this, [=] {
|
||||
handleControllerError(error);
|
||||
});
|
||||
}
|
||||
|
||||
void Call::handleRequestError(const RPCError &error) {
|
||||
@@ -714,9 +797,12 @@ void Call::handleRequestError(const RPCError &error) {
|
||||
}
|
||||
|
||||
void Call::handleControllerError(int error) {
|
||||
if (error == TGVOIP_ERROR_INCOMPATIBLE) {
|
||||
Ui::show(Box<InformBox>(Lang::Hard::CallErrorIncompatible().replace("{user}", App::peerName(_user))));
|
||||
} else if (error == TGVOIP_ERROR_AUDIO_IO) {
|
||||
if (error == tgvoip::ERROR_INCOMPATIBLE) {
|
||||
Ui::show(Box<InformBox>(
|
||||
Lang::Hard::CallErrorIncompatible().replace(
|
||||
"{user}",
|
||||
App::peerName(_user))));
|
||||
} else if (error == tgvoip::ERROR_AUDIO_IO) {
|
||||
Ui::show(Box<InformBox>(lang(lng_call_error_audio_io)));
|
||||
}
|
||||
finish(FinishType::Failed);
|
||||
@@ -728,6 +814,7 @@ void Call::destroyController() {
|
||||
_controller.reset();
|
||||
DEBUG_LOG(("Call Info: Call controller destroyed."));
|
||||
}
|
||||
setSignalBarCount(kSignalBarFinished);
|
||||
}
|
||||
|
||||
Call::~Call() {
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/bytes.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "mtproto/auth_key.h"
|
||||
|
||||
@@ -27,7 +28,7 @@ namespace Calls {
|
||||
struct DhConfig {
|
||||
int32 version = 0;
|
||||
int32 g = 0;
|
||||
std::vector<gsl::byte> p;
|
||||
bytes::vector p;
|
||||
};
|
||||
|
||||
class Call : public base::has_weak_ptr, private MTP::Sender {
|
||||
@@ -48,8 +49,6 @@ public:
|
||||
|
||||
};
|
||||
|
||||
static constexpr auto kRandomPowerSize = 256;
|
||||
static constexpr auto kSha256Size = 32;
|
||||
static constexpr auto kSoundSampleMs = 100;
|
||||
|
||||
enum class Type {
|
||||
@@ -66,7 +65,7 @@ public:
|
||||
}
|
||||
bool isIncomingWaiting() const;
|
||||
|
||||
void start(base::const_byte_span random);
|
||||
void start(bytes::const_span random);
|
||||
bool handleUpdate(const MTPPhoneCall &call);
|
||||
|
||||
enum State {
|
||||
@@ -93,6 +92,13 @@ public:
|
||||
return _stateChanged;
|
||||
}
|
||||
|
||||
static constexpr auto kSignalBarStarting = -1;
|
||||
static constexpr auto kSignalBarFinished = -2;
|
||||
static constexpr auto kSignalBarCount = 4;
|
||||
base::Observable<int> &signalBarCountChanged() {
|
||||
return _signalBarCountChanged;
|
||||
}
|
||||
|
||||
void setMute(bool mute);
|
||||
bool isMute() const {
|
||||
return _mute;
|
||||
@@ -109,13 +115,30 @@ public:
|
||||
void redial();
|
||||
|
||||
bool isKeyShaForFingerprintReady() const;
|
||||
std::array<gsl::byte, kSha256Size> getKeyShaForFingerprint() const;
|
||||
bytes::vector getKeyShaForFingerprint() const;
|
||||
|
||||
QString getDebugLog() const;
|
||||
|
||||
~Call();
|
||||
|
||||
private:
|
||||
class ControllerPointer {
|
||||
public:
|
||||
void create();
|
||||
void reset();
|
||||
bool empty() const;
|
||||
|
||||
bool operator==(std::nullptr_t) const;
|
||||
explicit operator bool() const;
|
||||
tgvoip::VoIPController *operator->() const;
|
||||
tgvoip::VoIPController &operator*() const;
|
||||
|
||||
~ControllerPointer();
|
||||
|
||||
private:
|
||||
std::unique_ptr<tgvoip::VoIPController> _data;
|
||||
|
||||
};
|
||||
enum class FinishType {
|
||||
None,
|
||||
Ended,
|
||||
@@ -128,8 +151,13 @@ private:
|
||||
void startIncoming();
|
||||
void startWaitingTrack();
|
||||
|
||||
void generateModExpFirst(base::const_byte_span randomSeed);
|
||||
void handleControllerStateChange(tgvoip::VoIPController *controller, int state);
|
||||
void generateModExpFirst(bytes::const_span randomSeed);
|
||||
void handleControllerStateChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int state);
|
||||
void handleControllerBarCountChange(
|
||||
tgvoip::VoIPController *controller,
|
||||
int count);
|
||||
void createAndStartController(const MTPDphoneCall &call);
|
||||
|
||||
template <typename T>
|
||||
@@ -142,6 +170,7 @@ private:
|
||||
void setState(State state);
|
||||
void setStateQueued(State state);
|
||||
void setFailedQueued(int error);
|
||||
void setSignalBarCount(int count);
|
||||
void destroyController();
|
||||
|
||||
not_null<Delegate*> _delegate;
|
||||
@@ -151,6 +180,8 @@ private:
|
||||
FinishType _finishAfterRequestingCall = FinishType::None;
|
||||
bool _answerAfterDhConfigReceived = false;
|
||||
base::Observable<State> _stateChanged;
|
||||
int _signalBarCount = kSignalBarStarting;
|
||||
base::Observable<int> _signalBarCountChanged;
|
||||
TimeMs _startTime = 0;
|
||||
base::DelayedCallTimer _finishByTimeoutTimer;
|
||||
base::Timer _discardByTimeoutTimer;
|
||||
@@ -159,10 +190,10 @@ private:
|
||||
base::Observable<bool> _muteChanged;
|
||||
|
||||
DhConfig _dhConfig;
|
||||
std::vector<gsl::byte> _ga;
|
||||
std::vector<gsl::byte> _gb;
|
||||
std::array<gsl::byte, kSha256Size> _gaHash;
|
||||
std::array<gsl::byte, kRandomPowerSize> _randomPower;
|
||||
bytes::vector _ga;
|
||||
bytes::vector _gb;
|
||||
bytes::vector _gaHash;
|
||||
bytes::vector _randomPower;
|
||||
MTP::AuthKey::Data _authKey;
|
||||
MTPPhoneCallProtocol _protocol;
|
||||
|
||||
@@ -170,7 +201,7 @@ private:
|
||||
uint64 _accessHash = 0;
|
||||
uint64 _keyFingerprint = 0;
|
||||
|
||||
std::unique_ptr<tgvoip::VoIPController> _controller;
|
||||
ControllerPointer _controller;
|
||||
|
||||
std::unique_ptr<Media::Audio::Track> _waitingTrack;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace Calls {
|
||||
namespace {
|
||||
|
||||
ushort Data[] = {
|
||||
const ushort Data[] = {
|
||||
0xd83d, 0xde09, 0xd83d, 0xde0d, 0xd83d, 0xde1b, 0xd83d, 0xde2d, 0xd83d, 0xde31, 0xd83d, 0xde21,
|
||||
0xd83d, 0xde0e, 0xd83d, 0xde34, 0xd83d, 0xde35, 0xd83d, 0xde08, 0xd83d, 0xde2c, 0xd83d, 0xde07,
|
||||
0xd83d, 0xde0f, 0xd83d, 0xdc6e, 0xd83d, 0xdc77, 0xd83d, 0xdc82, 0xd83d, 0xdc76, 0xd83d, 0xdc68,
|
||||
@@ -69,7 +69,7 @@ ushort Data[] = {
|
||||
0x0030, 0x20e3, 0xd83d, 0xdd1f, 0x2757, 0x2753, 0x2665, 0x2666, 0xd83d, 0xdcaf, 0xd83d, 0xdd17,
|
||||
0xd83d, 0xdd31, 0xd83d, 0xdd34, 0xd83d, 0xdd35, 0xd83d, 0xdd36, 0xd83d, 0xdd37 };
|
||||
|
||||
ushort Offsets[] = {
|
||||
const ushort Offsets[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22,
|
||||
24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
|
||||
48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70,
|
||||
@@ -99,7 +99,7 @@ ushort Offsets[] = {
|
||||
620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 641,
|
||||
642, 643, 644, 646, 648, 650, 652, 654, 656, 658 };
|
||||
|
||||
uint64 ComputeEmojiIndex(base::const_byte_span bytes) {
|
||||
uint64 ComputeEmojiIndex(bytes::const_span bytes) {
|
||||
Expects(bytes.size() == 8);
|
||||
return ((gsl::to_integer<uint64>(bytes[0]) & 0x7F) << 56)
|
||||
| (gsl::to_integer<uint64>(bytes[1]) << 48)
|
||||
@@ -119,7 +119,9 @@ std::vector<EmojiPtr> ComputeEmojiFingerprint(not_null<Call*> call) {
|
||||
for (auto index = 0; index != EmojiCount; ++index) {
|
||||
auto offset = Offsets[index];
|
||||
auto size = Offsets[index + 1] - offset;
|
||||
auto string = QString::fromRawData(reinterpret_cast<QChar*>(Data + offset), size);
|
||||
auto string = QString::fromRawData(
|
||||
reinterpret_cast<const QChar*>(Data + offset),
|
||||
size);
|
||||
auto emoji = Ui::Emoji::Find(string);
|
||||
Assert(emoji != nullptr);
|
||||
}
|
||||
@@ -131,7 +133,9 @@ std::vector<EmojiPtr> ComputeEmojiFingerprint(not_null<Call*> call) {
|
||||
auto index = value % EmojiCount;
|
||||
auto offset = Offsets[index];
|
||||
auto size = Offsets[index + 1] - offset;
|
||||
auto string = QString::fromRawData(reinterpret_cast<QChar*>(Data + offset), size);
|
||||
auto string = QString::fromRawData(
|
||||
reinterpret_cast<const QChar*>(Data + offset),
|
||||
size);
|
||||
auto emoji = Ui::Emoji::Find(string);
|
||||
Assert(emoji != nullptr);
|
||||
result.push_back(emoji);
|
||||
|
||||
@@ -126,26 +126,26 @@ void Instance::refreshDhConfig() {
|
||||
Expects(_currentCall != nullptr);
|
||||
request(MTPmessages_GetDhConfig(
|
||||
MTP_int(_dhConfig.version),
|
||||
MTP_int(Call::kRandomPowerSize)
|
||||
MTP_int(MTP::ModExpFirst::kRandomPowerSize)
|
||||
)).done([this, call = base::make_weak(_currentCall)](
|
||||
const MTPmessages_DhConfig &result) {
|
||||
auto random = base::const_byte_span();
|
||||
auto random = bytes::const_span();
|
||||
switch (result.type()) {
|
||||
case mtpc_messages_dhConfig: {
|
||||
auto &config = result.c_messages_dhConfig();
|
||||
if (!MTP::IsPrimeAndGood(bytesFromMTP(config.vp), config.vg.v)) {
|
||||
if (!MTP::IsPrimeAndGood(bytes::make_span(config.vp.v), config.vg.v)) {
|
||||
LOG(("API Error: bad p/g received in dhConfig."));
|
||||
callFailed(call.get());
|
||||
return;
|
||||
}
|
||||
_dhConfig.g = config.vg.v;
|
||||
_dhConfig.p = byteVectorFromMTP(config.vp);
|
||||
random = bytesFromMTP(config.vrandom);
|
||||
_dhConfig.p = bytes::make_vector(config.vp.v);
|
||||
random = bytes::make_span(config.vrandom.v);
|
||||
} break;
|
||||
|
||||
case mtpc_messages_dhConfigNotModified: {
|
||||
auto &config = result.c_messages_dhConfigNotModified();
|
||||
random = bytesFromMTP(config.vrandom);
|
||||
random = bytes::make_span(config.vrandom.v);
|
||||
if (!_dhConfig.g || _dhConfig.p.empty()) {
|
||||
LOG(("API Error: dhConfigNotModified on zero version."));
|
||||
callFailed(call.get());
|
||||
@@ -156,7 +156,7 @@ void Instance::refreshDhConfig() {
|
||||
default: Unexpected("Type in messages.getDhConfig");
|
||||
}
|
||||
|
||||
if (random.size() != Call::kRandomPowerSize) {
|
||||
if (random.size() != MTP::ModExpFirst::kRandomPowerSize) {
|
||||
LOG(("API Error: dhConfig random bytes wrong size: %1").arg(random.size()));
|
||||
callFailed(call.get());
|
||||
return;
|
||||
@@ -186,7 +186,7 @@ void Instance::refreshServerConfig() {
|
||||
_lastServerConfigUpdateTime = getms(true);
|
||||
|
||||
auto configUpdate = std::map<std::string, std::string>();
|
||||
auto bytes = bytesFromMTP(result.c_dataJSON().vdata);
|
||||
auto bytes = bytes::make_span(result.c_dataJSON().vdata.v);
|
||||
auto error = QJsonParseError { 0, QJsonParseError::NoError };
|
||||
auto document = QJsonDocument::fromJson(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes.data()), bytes.size()), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
|
||||
@@ -67,6 +67,66 @@ private:
|
||||
|
||||
};
|
||||
|
||||
SignalBars::SignalBars(
|
||||
QWidget *parent,
|
||||
not_null<Call*> call,
|
||||
const style::CallSignalBars &st,
|
||||
Fn<void()> displayedChangedCallback)
|
||||
: RpWidget(parent)
|
||||
, _st(st)
|
||||
, _displayedChangedCallback(std::move(displayedChangedCallback)) {
|
||||
resize(
|
||||
_st.width + (_st.width + _st.skip) * (Call::kSignalBarCount - 1),
|
||||
_st.width * Call::kSignalBarCount);
|
||||
subscribe(call->signalBarCountChanged(), [=](int count) {
|
||||
changed(count);
|
||||
});
|
||||
}
|
||||
|
||||
bool SignalBars::isDisplayed() const {
|
||||
return (_count >= 0);
|
||||
}
|
||||
|
||||
void SignalBars::paintEvent(QPaintEvent *e) {
|
||||
if (!isDisplayed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Painter p(this);
|
||||
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(_st.color);
|
||||
for (auto i = 0; i < Call::kSignalBarCount; ++i) {
|
||||
p.setOpacity((i < _count) ? 1. : _st.inactiveOpacity);
|
||||
const auto barHeight = (i + 1) * _st.width;
|
||||
const auto barLeft = i * (_st.width + _st.skip);
|
||||
const auto barTop = height() - barHeight;
|
||||
p.drawRoundedRect(
|
||||
barLeft,
|
||||
barTop,
|
||||
_st.width,
|
||||
barHeight,
|
||||
_st.radius,
|
||||
_st.radius);
|
||||
}
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
void SignalBars::changed(int count) {
|
||||
if (_count == Call::kSignalBarFinished) {
|
||||
return;
|
||||
}
|
||||
if (_count != count) {
|
||||
const auto wasDisplayed = isDisplayed();
|
||||
_count = count;
|
||||
if (isDisplayed() != wasDisplayed && _displayedChangedCallback) {
|
||||
_displayedChangedCallback();
|
||||
}
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
Panel::Button::Button(QWidget *parent, const style::CallButton &stFrom, const style::CallButton *stTo) : Ui::RippleButton(parent, stFrom.button.ripple)
|
||||
, _stFrom(&stFrom)
|
||||
, _stTo(stTo) {
|
||||
@@ -238,7 +298,8 @@ Panel::Panel(not_null<Call*> call)
|
||||
, _cancel(this, object_ptr<Button>(this, st::callCancel))
|
||||
, _mute(this, st::callMuteToggle)
|
||||
, _name(this, st::callName)
|
||||
, _status(this, st::callStatus) {
|
||||
, _status(this, st::callStatus)
|
||||
, _signalBars(this, call, st::callPanelSignalBars) {
|
||||
_decline->setDuration(st::callPanelDuration);
|
||||
_cancel->setDuration(st::callPanelDuration);
|
||||
|
||||
@@ -337,10 +398,18 @@ void Panel::initControls() {
|
||||
void Panel::reinitControls() {
|
||||
Expects(_call != nullptr);
|
||||
|
||||
unsubscribe(_stateChangedSubscription);
|
||||
_stateChangedSubscription = subscribe(_call->stateChanged(), [this](State state) { stateChanged(state); });
|
||||
unsubscribe(base::take(_stateChangedSubscription));
|
||||
_stateChangedSubscription = subscribe(
|
||||
_call->stateChanged(),
|
||||
[=](State state) { stateChanged(state); });
|
||||
stateChanged(_call->state());
|
||||
|
||||
_signalBars.create(
|
||||
this,
|
||||
_call,
|
||||
st::callPanelSignalBars,
|
||||
[=] { rtlupdate(signalBarsRect()); });
|
||||
|
||||
_name->setText(App::peerName(_call->user()));
|
||||
updateStatusText(_call->state());
|
||||
}
|
||||
@@ -531,7 +600,7 @@ void Panel::createBottomImage() {
|
||||
p.setBrush(st::callBg);
|
||||
p.setPen(Qt::NoPen);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawRoundedRect(myrtlrect(_padding.left(), -st::historyMessageRadius, st::callWidth, bottomHeight - _padding.bottom() + st::historyMessageRadius), st::historyMessageRadius, st::historyMessageRadius);
|
||||
p.drawRoundedRect(myrtlrect(_padding.left(), -st::callRadius, st::callWidth, bottomHeight - _padding.bottom() + st::callRadius), st::callRadius, st::callRadius);
|
||||
}
|
||||
_bottomCache = App::pixmapFromImageInPlace(std::move(image));
|
||||
}
|
||||
@@ -551,7 +620,7 @@ void Panel::createDefaultCacheImage() {
|
||||
p.setBrush(st::callBg);
|
||||
p.setPen(Qt::NoPen);
|
||||
PainterHighQualityEnabler hq(p);
|
||||
p.drawRoundedRect(myrtlrect(inner), st::historyMessageRadius, st::historyMessageRadius);
|
||||
p.drawRoundedRect(myrtlrect(inner), st::callRadius, st::callRadius);
|
||||
}
|
||||
_cache = App::pixmapFromImageInPlace(std::move(cache));
|
||||
}
|
||||
@@ -585,6 +654,12 @@ void Panel::updateControlsGeometry() {
|
||||
updateHangupGeometry();
|
||||
|
||||
_mute->moveToRight(_padding.right() + st::callMuteRight, controlsTop);
|
||||
|
||||
const auto skip = st::callSignalMargin + st::callSignalPadding;
|
||||
const auto delta = (_signalBars->width() - _signalBars->height());
|
||||
_signalBars->moveToLeft(
|
||||
_padding.left() + skip,
|
||||
_padding.top() + skip + delta / 2);
|
||||
}
|
||||
|
||||
void Panel::updateHangupGeometry() {
|
||||
@@ -637,6 +712,10 @@ void Panel::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(0, _contentTop, width(), height() - _contentTop, brush);
|
||||
}
|
||||
|
||||
if (_signalBars->isDisplayed()) {
|
||||
paintSignalBarsBg(p);
|
||||
}
|
||||
|
||||
if (!_fingerprint.empty()) {
|
||||
App::roundRect(p, _fingerprintArea, st::callFingerprintBg, ImageRoundRadius::Small);
|
||||
|
||||
@@ -651,6 +730,23 @@ void Panel::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
QRect Panel::signalBarsRect() const {
|
||||
const auto size = 2 * st::callSignalPadding + _signalBars->width();
|
||||
return QRect(
|
||||
_padding.left() + st::callSignalMargin,
|
||||
_padding.top() + st::callSignalMargin,
|
||||
size,
|
||||
size);
|
||||
}
|
||||
|
||||
void Panel::paintSignalBarsBg(Painter &p) {
|
||||
App::roundRect(
|
||||
p,
|
||||
signalBarsRect(),
|
||||
st::callFingerprintBg,
|
||||
ImageRoundRadius::Small);
|
||||
}
|
||||
|
||||
void Panel::closeEvent(QCloseEvent *e) {
|
||||
if (_call) {
|
||||
_call->hangup();
|
||||
|
||||
@@ -20,8 +20,34 @@ template <typename Widget>
|
||||
class FadeWrap;
|
||||
} // namespace Ui
|
||||
|
||||
namespace style {
|
||||
struct CallSignalBars;
|
||||
} // namespace style
|
||||
|
||||
namespace Calls {
|
||||
|
||||
class SignalBars : public Ui::RpWidget, private base::Subscriber {
|
||||
public:
|
||||
SignalBars(
|
||||
QWidget *parent,
|
||||
not_null<Call*> call,
|
||||
const style::CallSignalBars &st,
|
||||
Fn<void()> displayedChangedCallback = nullptr);
|
||||
|
||||
bool isDisplayed() const;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
void changed(int count);
|
||||
|
||||
const style::CallSignalBars &_st;
|
||||
int _count = Call::kSignalBarStarting;
|
||||
Fn<void()> _displayedChangedCallback;
|
||||
|
||||
};
|
||||
|
||||
class Panel
|
||||
: public Ui::RpWidget
|
||||
, private base::Subscriber
|
||||
@@ -67,6 +93,8 @@ private:
|
||||
void refreshUserPhoto();
|
||||
bool isGoodUserPhoto(PhotoData *photo);
|
||||
void createUserpicCache(ImagePtr image);
|
||||
QRect signalBarsRect() const;
|
||||
void paintSignalBarsBg(Painter &p);
|
||||
|
||||
void updateControlsGeometry();
|
||||
void updateHangupGeometry();
|
||||
@@ -102,6 +130,7 @@ private:
|
||||
object_ptr<Ui::IconButton> _mute;
|
||||
object_ptr<Ui::FlatLabel> _name;
|
||||
object_ptr<Ui::FlatLabel> _status;
|
||||
object_ptr<SignalBars> _signalBars;
|
||||
std::vector<EmojiPtr> _fingerprint;
|
||||
QRect _fingerprintArea;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "calls/calls_call.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "calls/calls_panel.h"
|
||||
#include "styles/style_boxes.h"
|
||||
#include "observer_peer.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
@@ -75,6 +76,7 @@ TopBar::TopBar(
|
||||
: RpWidget(parent)
|
||||
, _call(call)
|
||||
, _durationLabel(this, st::callBarLabel)
|
||||
, _signalBars(this, _call.get(), st::callBarSignalBars)
|
||||
, _fullInfoLabel(this, st::callBarInfoLabel)
|
||||
, _shortInfoLabel(this, st::callBarInfoLabel)
|
||||
, _hangupLabel(this, st::callBarLabel, lang(lng_call_bar_hangup).toUpper())
|
||||
@@ -106,7 +108,8 @@ void TopBar::initControls() {
|
||||
setInfoLabels();
|
||||
_info->setClickedCallback([this] {
|
||||
if (auto call = _call.get()) {
|
||||
if (cDebug() && (_info->clickModifiers() & Qt::ControlModifier)) {
|
||||
if (Logs::DebugEnabled()
|
||||
&& (_info->clickModifiers() & Qt::ControlModifier)) {
|
||||
Ui::show(Box<DebugInfoBox>(_call));
|
||||
} else {
|
||||
Current().showInfoPanel(call);
|
||||
@@ -169,14 +172,23 @@ void TopBar::resizeEvent(QResizeEvent *e) {
|
||||
|
||||
void TopBar::updateControlsGeometry() {
|
||||
auto left = 0;
|
||||
_mute->moveToLeft(left, 0); left += _mute->width();
|
||||
_durationLabel->moveToLeft(left, st::callBarLabelTop); left += _durationLabel->width() + st::callBarSkip;
|
||||
_mute->moveToLeft(left, 0);
|
||||
left += _mute->width();
|
||||
_durationLabel->moveToLeft(left, st::callBarLabelTop);
|
||||
left += _durationLabel->width() + st::callBarSkip;
|
||||
_signalBars->moveToLeft(left, (height() - _signalBars->height()) / 2);
|
||||
left += _signalBars->width() + st::callBarSkip;
|
||||
|
||||
auto right = st::callBarRightSkip;
|
||||
_hangupLabel->moveToRight(right, st::callBarLabelTop); right += _hangupLabel->width();
|
||||
_hangupLabel->moveToRight(right, st::callBarLabelTop);
|
||||
right += _hangupLabel->width();
|
||||
right += st::callBarHangup.width;
|
||||
_hangup->setGeometryToRight(0, 0, right, height());
|
||||
_info->setGeometryToLeft(_mute->width(), 0, width() - _mute->width() - _hangup->width(), height());
|
||||
_info->setGeometryToLeft(
|
||||
_mute->width(),
|
||||
0,
|
||||
width() - _mute->width() - _hangup->width(),
|
||||
height());
|
||||
|
||||
auto fullWidth = _fullInfoLabel->naturalWidth();
|
||||
auto showFull = (left + fullWidth + right <= width());
|
||||
|
||||
@@ -21,6 +21,7 @@ class FlatLabel;
|
||||
namespace Calls {
|
||||
|
||||
class Call;
|
||||
class SignalBars;
|
||||
|
||||
class TopBar : public Ui::RpWidget, private base::Subscriber {
|
||||
public:
|
||||
@@ -45,6 +46,7 @@ private:
|
||||
|
||||
bool _muted = false;
|
||||
object_ptr<Ui::LabelSimple> _durationLabel;
|
||||
object_ptr<SignalBars> _signalBars;
|
||||
object_ptr<Ui::FlatLabel> _fullInfoLabel;
|
||||
object_ptr<Ui::FlatLabel> _shortInfoLabel;
|
||||
object_ptr<Ui::LabelSimple> _hangupLabel;
|
||||
|
||||
@@ -615,7 +615,10 @@ QRect EmojiListWidget::emojiRect(int section, int sel) {
|
||||
|
||||
void EmojiListWidget::onColorSelected(EmojiPtr emoji) {
|
||||
if (emoji->hasVariants()) {
|
||||
cRefEmojiVariants().insert(emoji->nonColoredId(), emoji->variantIndex(emoji));
|
||||
cRefEmojiVariants().insert(
|
||||
emoji->nonColoredId(),
|
||||
emoji->variantIndex(emoji));
|
||||
Auth().saveSettingsDelayed();
|
||||
}
|
||||
if (_pickerSel >= 0) {
|
||||
auto section = (_pickerSel / MatrixRowShift);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user