Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df9ec4b466 | ||
|
|
4f9507ed97 | ||
|
|
f761b6aa9e | ||
|
|
168a7ce2e5 | ||
|
|
4b763a76df | ||
|
|
8764da787b | ||
|
|
7d8ba15252 | ||
|
|
96c0c30f7c | ||
|
|
bb6ab5314c | ||
|
|
e3c6abfc3d | ||
|
|
5c5bccae0b | ||
|
|
296e009808 | ||
|
|
4d84781a65 | ||
|
|
710b9bf454 | ||
|
|
0f54315495 | ||
|
|
c3fc91a6fc | ||
|
|
d2048f3c25 | ||
|
|
cc2c13d018 | ||
|
|
48c1576d7f | ||
|
|
d2fa8ef0b0 | ||
|
|
97b576f446 | ||
|
|
257dfa6b3f | ||
|
|
678d2a58c5 | ||
|
|
e0431d270b | ||
|
|
7797e5a3b7 | ||
|
|
d15b0cdb08 | ||
|
|
1af2769209 | ||
|
|
e6906b84f3 | ||
|
|
ca0f6c7ded | ||
|
|
f9ff676e57 | ||
|
|
db7041f2dc | ||
|
|
ad1f089802 | ||
|
|
62c812858e | ||
|
|
4bf66cb6e9 | ||
|
|
95fee543ec | ||
|
|
df4daca15b | ||
|
|
f794d8dbd8 | ||
|
|
9935a36c3d | ||
|
|
a7c77682d7 | ||
|
|
8bbea976ea | ||
|
|
900d1ddb36 | ||
|
|
8e99135f37 | ||
|
|
dc9483e07a | ||
|
|
48e913bf2c | ||
|
|
993cb987a6 | ||
|
|
65f968ec1b | ||
|
|
93f6d4b6e7 | ||
|
|
7482025c10 | ||
|
|
909acb25fd | ||
|
|
4a9db99082 | ||
|
|
a2606c4fc4 | ||
|
|
647c609bf8 | ||
|
|
cf98025177 | ||
|
|
e0f20e9f82 | ||
|
|
597a5c9d75 | ||
|
|
d055908f4f | ||
|
|
f3eac6b259 | ||
|
|
87d6081408 | ||
|
|
dd53bd1c55 | ||
|
|
3ff033cdf3 | ||
|
|
c1c3b6a7e5 | ||
|
|
64d5a6acd5 | ||
|
|
e5b2e0a6b5 | ||
|
|
9895b45293 | ||
|
|
811fc43b63 | ||
|
|
9dcfa3ad6e | ||
|
|
67bda19458 | ||
|
|
6c38919c3d | ||
|
|
ce9445287c | ||
|
|
d4bd8862bd | ||
|
|
6904e023d3 | ||
|
|
91a7a77bb0 | ||
|
|
d9306e3e30 | ||
|
|
efdd3df129 | ||
|
|
63098d3c7d | ||
|
|
aa5781b550 | ||
|
|
d6e1862c08 | ||
|
|
9aa2831fef | ||
|
|
70eb29c1a9 | ||
|
|
13e07b1623 | ||
|
|
27ce1f8d44 | ||
|
|
38c20fc3c2 | ||
|
|
4a32b00068 | ||
|
|
3406f88fdc | ||
|
|
c96cb37680 | ||
|
|
0d415837a0 | ||
|
|
9dc48522d8 | ||
|
|
31b82a5d92 | ||
|
|
87ab4d9dd1 | ||
|
|
b6e7625016 | ||
|
|
c5e6bfce95 | ||
|
|
7a849b2899 | ||
|
|
999fa39d7c | ||
|
|
7de15ce5cf | ||
|
|
f792b0052f | ||
|
|
57d0b1d215 | ||
|
|
7691654cb8 | ||
|
|
c76e4b6b3c | ||
|
|
bda39cc6f6 | ||
|
|
060cdfea86 | ||
|
|
b1cc7b25ba | ||
|
|
1e0fe70dc3 | ||
|
|
8ed167c5fa | ||
|
|
dabf8414be | ||
|
|
a0eb64428e | ||
|
|
dd1beb1d91 | ||
|
|
bb35d71fdc | ||
|
|
42a7e86e51 | ||
|
|
2f3540dadc | ||
|
|
eb00641dfa | ||
|
|
bfe7bf2c11 | ||
|
|
e88c575d4a | ||
|
|
0de9c62675 | ||
|
|
9dc3847dbe | ||
|
|
9dc03c4f0f | ||
|
|
def21367a3 | ||
|
|
33fe1b6389 | ||
|
|
76cb5677b2 | ||
|
|
8c3b7f6417 | ||
|
|
e6c0f0f774 | ||
|
|
6bd5301828 | ||
|
|
73c0c4507a | ||
|
|
506b0806d6 |
@@ -486,6 +486,9 @@ buildOpenAL() {
|
||||
-D CMAKE_INSTALL_PREFIX=$OPENAL_PATH \
|
||||
-D CMAKE_BUILD_TYPE=Release \
|
||||
-D LIBTYPE=STATIC \
|
||||
-D ALSOFT_EXAMPLES=OFF \
|
||||
-D ALSOFT_TESTS=OFF \
|
||||
-D ALSOFT_UTILS=OFF \
|
||||
..
|
||||
make $MAKE_ARGS
|
||||
sudo make install
|
||||
@@ -608,7 +611,7 @@ buildCustomQt() {
|
||||
./configure -prefix $QT_PATH -release -opensource -confirm-license -qt-zlib \
|
||||
-qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb \
|
||||
-qt-xkbcommon-x11 -no-opengl -no-gtkstyle -static \
|
||||
-nomake examples -nomake tests \
|
||||
-nomake examples -nomake tests -no-mirclient \
|
||||
-dbus-runtime -no-gstreamer -no-mtdev # <- Not sure about these
|
||||
make $MAKE_ARGS
|
||||
sudo make install
|
||||
|
||||
@@ -15,7 +15,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
|
||||
* Windows XP - Windows 10 (**not** RT)
|
||||
* Mac OS X 10.8 - Mac OS X 10.11
|
||||
* Mac OS X 10.6 - Mac OS X 10.7 (separate build)
|
||||
* Ubuntu 12.04 - Ubuntu 16.04
|
||||
* Ubuntu 12.04 - Ubuntu 18.04
|
||||
* Fedora 22 - Fedora 24
|
||||
|
||||
## Third-party
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/configure b/configure
|
||||
index cb8d78f..cadb3f0 100755
|
||||
index cb8d78fd3cb..cadb3f0a880 100755
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
@@ -511,7 +511,8 @@ if [ "$BUILD_ON_MAC" = "yes" ]; then
|
||||
@@ -13,7 +13,7 @@ index cb8d78f..cadb3f0 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 086510d..c485967 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 086510d..c485967 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 0cc8cd6..ca9725b 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 0cc8cd6..ca9725b 100644
|
||||
}
|
||||
|
||||
diff --git a/src/gui/image/qbmphandler.cpp b/src/gui/image/qbmphandler.cpp
|
||||
index bb79a13..5d595bc 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 bb79a13..5d595bc 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 ebff950..4300ca4 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 ebff950..4300ca4 100644
|
||||
|
||||
// Make sure we're inside the viewport.
|
||||
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
|
||||
index 4879ae5..56cdcba 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 4879ae5..56cdcba 100644
|
||||
|
||||
inline void resetRightBearing()
|
||||
diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h
|
||||
index cbe42c3..b273db7 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 cbe42c3..b273db7 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 ca7afb7..25ae500 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 ca7afb7..25ae500 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 92358ec..694fee7 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 92358ec..694fee7 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 b81b9a0..4e59e83 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)
|
||||
@@ -256,8 +283,41 @@ index b81b9a0..4e59e83 100644
|
||||
CGEventPost(kCGHIDEventTap, e);
|
||||
CFRelease(e);
|
||||
}
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
|
||||
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
|
||||
// Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev)
|
||||
OSStatus err = noErr;
|
||||
|
||||
- require_action(inContext != NULL, InvalidContext, err = paramErr);
|
||||
- require_action(inBounds != NULL, InvalidBounds, err = paramErr);
|
||||
- require_action(inImage != NULL, InvalidImage, err = paramErr);
|
||||
+ // Patch: Fix build on latest Xcode.
|
||||
+ //require_action(inContext != NULL, InvalidContext, err = paramErr);
|
||||
+ //require_action(inBounds != NULL, InvalidBounds, err = paramErr);
|
||||
+ //require_action(inImage != NULL, InvalidImage, err = paramErr);
|
||||
|
||||
CGContextSaveGState( inContext );
|
||||
CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds));
|
||||
@@ -660,9 +661,11 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm
|
||||
CGContextDrawImage(inContext, *inBounds, inImage);
|
||||
|
||||
CGContextRestoreGState(inContext);
|
||||
-InvalidImage:
|
||||
-InvalidBounds:
|
||||
-InvalidContext:
|
||||
+
|
||||
+// Patch: Fix build on latest Xcode.
|
||||
+//InvalidImage:
|
||||
+//InvalidBounds:
|
||||
+//InvalidContext:
|
||||
return err;
|
||||
}
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
|
||||
index 9fd05a6..dea6072 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()
|
||||
@@ -288,7 +348,7 @@ index 9fd05a6..dea6072 100644
|
||||
|
||||
QCocoaScreen *QCocoaIntegration::screenAtIndex(int index)
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm
|
||||
index e46eaff..c62db53 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()
|
||||
@@ -315,7 +375,7 @@ index e46eaff..c62db53 100644
|
||||
}
|
||||
return ret;
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index 83c960d..03ae969 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
|
||||
@@ -483,7 +543,7 @@ index 83c960d..03ae969 100755
|
||||
}
|
||||
@end
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
|
||||
index 4d0458a..3357a5e 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)
|
||||
@@ -546,7 +606,7 @@ index 4d0458a..3357a5e 100644
|
||||
[iconButton setImage:image];
|
||||
[image release];
|
||||
diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm
|
||||
index a18ee7f..1f91feb 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;
|
||||
@@ -615,7 +675,7 @@ index a18ee7f..1f91feb 100644
|
||||
}
|
||||
return [super performKeyEquivalent:nsevent];
|
||||
diff --git a/src/tools/qlalr/lalr.cpp b/src/tools/qlalr/lalr.cpp
|
||||
index c680764..e2a7aaf 100644
|
||||
index c68076477f3..e2a7aafa586 100644
|
||||
--- a/src/tools/qlalr/lalr.cpp
|
||||
+++ b/src/tools/qlalr/lalr.cpp
|
||||
@@ -246,11 +246,13 @@ void Grammar::buildExtendedGrammar ()
|
||||
@@ -655,7 +715,7 @@ index c680764..e2a7aaf 100644
|
||||
continue;
|
||||
|
||||
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
|
||||
index 7396808..7178aec 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,
|
||||
@@ -708,7 +768,7 @@ index 7396808..7178aec 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 0845a5e..5735cb6 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
|
||||
@@ -726,7 +786,7 @@ index 0845a5e..5735cb6 100644
|
||||
}
|
||||
|
||||
diff --git a/src/widgets/util/qsystemtrayicon_qpa.cpp b/src/widgets/util/qsystemtrayicon_qpa.cpp
|
||||
index f98aeaf..00c0734 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()
|
||||
@@ -755,7 +815,7 @@ index f98aeaf..00c0734 100644
|
||||
}
|
||||
|
||||
diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp
|
||||
index 75f3059..980f2be 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)
|
||||
@@ -769,7 +829,7 @@ index 75f3059..980f2be 100644
|
||||
#ifndef QT_NO_COMPLETER
|
||||
complete(event->key());
|
||||
diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp
|
||||
index 96438a0..b0b7206 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
|
||||
|
||||
@@ -215,8 +215,6 @@ emojiReplaceHeight: 56px;
|
||||
emojiReplaceInnerHeight: 42px;
|
||||
emojiReplacePadding: 14px;
|
||||
|
||||
connectingPadding: margins(5px, 5px, 5px, 5px);
|
||||
|
||||
dragFont: font(28px semibold);
|
||||
dragSubfont: font(20px semibold);
|
||||
dragColor: windowSubTextFg;
|
||||
|
||||
BIN
Telegram/Resources/icons/connecting_body.png
Normal file
|
After Width: | Height: | Size: 95 B |
BIN
Telegram/Resources/icons/connecting_body@2x.png
Normal file
|
After Width: | Height: | Size: 112 B |
BIN
Telegram/Resources/icons/connecting_body_shadow.png
Normal file
|
After Width: | Height: | Size: 117 B |
BIN
Telegram/Resources/icons/connecting_body_shadow@2x.png
Normal file
|
After Width: | Height: | Size: 151 B |
BIN
Telegram/Resources/icons/connecting_left.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
Telegram/Resources/icons/connecting_left@2x.png
Normal file
|
After Width: | Height: | Size: 717 B |
BIN
Telegram/Resources/icons/connecting_left_shadow.png
Normal file
|
After Width: | Height: | Size: 550 B |
BIN
Telegram/Resources/icons/connecting_left_shadow@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/connecting_right.png
Normal file
|
After Width: | Height: | Size: 347 B |
BIN
Telegram/Resources/icons/connecting_right@2x.png
Normal file
|
After Width: | Height: | Size: 679 B |
BIN
Telegram/Resources/icons/connecting_right_shadow.png
Normal file
|
After Width: | Height: | Size: 573 B |
BIN
Telegram/Resources/icons/connecting_right_shadow@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/passport_ready.png
Normal file
|
After Width: | Height: | Size: 369 B |
BIN
Telegram/Resources/icons/passport_ready@2x.png
Normal file
|
After Width: | Height: | Size: 655 B |
BIN
Telegram/Resources/icons/proxy_off.png
Normal file
|
After Width: | Height: | Size: 529 B |
BIN
Telegram/Resources/icons/proxy_off@2x.png
Normal file
|
After Width: | Height: | Size: 1012 B |
BIN
Telegram/Resources/icons/proxy_on.png
Normal file
|
After Width: | Height: | Size: 584 B |
BIN
Telegram/Resources/icons/proxy_on@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@@ -296,6 +296,8 @@ 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";
|
||||
"lng_settings_send_ctrlenter" = "Send by Ctrl+Enter";
|
||||
@@ -417,8 +419,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_connection_port_ph" = "Port";
|
||||
"lng_connection_user_ph" = "Username";
|
||||
"lng_connection_password_ph" = "Password";
|
||||
"lng_connection_proxy_secret_ph" = "Secret";
|
||||
"lng_connection_save" = "Save";
|
||||
|
||||
"lng_proxy_settings" = "Proxy settings";
|
||||
"lng_proxy_use" = "Use proxy";
|
||||
"lng_proxy_use_for_calls" = "Use proxy for calls";
|
||||
"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_checking" = "checking";
|
||||
"lng_proxy_connecting" = "connecting";
|
||||
"lng_proxy_available" = "available (ping: {ping}ms)";
|
||||
"lng_proxy_unavailable" = "not available";
|
||||
"lng_proxy_edit" = "Edit proxy";
|
||||
"lng_proxy_menu_edit" = "Edit";
|
||||
"lng_proxy_menu_delete" = "Delete";
|
||||
"lng_proxy_menu_restore" = "Restore";
|
||||
"lng_proxy_edit_share" = "Share";
|
||||
"lng_proxy_address_label" = "Socket address";
|
||||
"lng_proxy_credentials_optional" = "Credentials (optional)";
|
||||
"lng_proxy_credentials" = "Credentials";
|
||||
"lng_proxy_description" = "Your saved proxy list will be here.";
|
||||
|
||||
"lng_settings_blocked_users" = "Blocked users";
|
||||
"lng_settings_last_seen_privacy" = "Last seen privacy";
|
||||
"lng_settings_calls_privacy" = "Phone calls privacy";
|
||||
@@ -667,6 +691,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";
|
||||
@@ -819,6 +844,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_channel_not_accessible" = "Sorry, this channel is not accessible.";
|
||||
"lng_group_not_accessible" = "Sorry, this group is not accessible.";
|
||||
"lng_group_full" = "Sorry, this group is full.";
|
||||
|
||||
"lng_channels_too_much_public_about" = "Sorry, you have reserved too many public usernames. You can revoke the link from one of your older groups or channels.";
|
||||
"lng_channels_too_much_public_revoke_confirm_group" = "Are you sure you want to revoke the link {link}?\n\nThe group «{group}» will become private.";
|
||||
@@ -1081,6 +1107,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";
|
||||
|
||||
@@ -107,8 +107,13 @@ new_session_created#9ec20908 first_msg_id:long unique_id:long server_salt:long =
|
||||
|
||||
http_wait#9299359f max_delay:int wait_after:int max_wait:int = HttpWait;
|
||||
|
||||
ipPort ipv4:int port:int = IpPort;
|
||||
help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
|
||||
//ipPort ipv4:int port:int = IpPort;
|
||||
//help.configSimple#d997c3c5 date:int expires:int dc_id:int ip_port_list:Vector<ipPort> = help.ConfigSimple;
|
||||
|
||||
ipPort#d433ad73 ipv4:int port:int = IpPort;
|
||||
ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
|
||||
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
|
||||
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
|
||||
|
||||
---functions---
|
||||
|
||||
@@ -220,7 +225,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat;
|
||||
chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#450b7115 flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat;
|
||||
channel#c88974ac flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat;
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
|
||||
@@ -306,7 +311,6 @@ inputPeerNotifySettings#38935eb2 flags:# show_previews:flags.0?true silent:flags
|
||||
peerNotifyEventsEmpty#add53cb3 = PeerNotifyEvents;
|
||||
peerNotifyEventsAll#6d1ded88 = PeerNotifyEvents;
|
||||
|
||||
peerNotifySettingsEmpty#70a68512 = PeerNotifySettings;
|
||||
peerNotifySettings#9acda4c0 flags:# show_previews:flags.0?true silent:flags.1?true mute_until:int sound:string = PeerNotifySettings;
|
||||
|
||||
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
|
||||
@@ -461,9 +465,9 @@ photos.photo#20212ca8 photo:Photo users:Vector<User> = photos.Photo;
|
||||
upload.file#96a18d5 type:storage.FileType mtime:int bytes:bytes = upload.File;
|
||||
upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes encryption_iv:bytes file_hashes:Vector<FileHash> = upload.File;
|
||||
|
||||
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int = DcOption;
|
||||
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
|
||||
|
||||
config#86b5778e flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config;
|
||||
config#eb7bb160 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config;
|
||||
|
||||
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
||||
|
||||
@@ -1220,4 +1224,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 76
|
||||
// LAYER 78
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.2.10.0" />
|
||||
Version="1.2.20.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,10,0
|
||||
PRODUCTVERSION 1,2,10,0
|
||||
FILEVERSION 1,2,20,0
|
||||
PRODUCTVERSION 1,2,20,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.10.0"
|
||||
VALUE "FileVersion", "1.2.20.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.2.10.0"
|
||||
VALUE "ProductVersion", "1.2.20.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,2,10,0
|
||||
PRODUCTVERSION 1,2,10,0
|
||||
FILEVERSION 1,2,20,0
|
||||
PRODUCTVERSION 1,2,20,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.10.0"
|
||||
VALUE "FileVersion", "1.2.20.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2018"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.2.10.0"
|
||||
VALUE "ProductVersion", "1.2.20.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -480,7 +480,7 @@ int main(int argc, char *argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString countBetaVersionSignature(quint64 version) { // duplicated in autoupdate.cpp
|
||||
QString countBetaVersionSignature(quint64 version) { // duplicated in autoupdater.cpp
|
||||
QByteArray cBetaPrivateKey(BetaPrivateKey);
|
||||
if (cBetaPrivateKey.isEmpty()) {
|
||||
cout << "Error: Trying to count beta version signature without beta private key!\n";
|
||||
|
||||
@@ -908,7 +908,7 @@ void ApiWrap::requestPeers(const QList<PeerData*> &peers) {
|
||||
channels.push_back((*i)->asChannel()->inputChannel);
|
||||
}
|
||||
}
|
||||
auto handleChats = [this](const MTPmessages_Chats &result) {
|
||||
auto handleChats = [=](const MTPmessages_Chats &result) {
|
||||
if (auto chats = Api::getChatsFromMessagesChats(result)) {
|
||||
App::feedChats(*chats);
|
||||
}
|
||||
@@ -920,7 +920,7 @@ void ApiWrap::requestPeers(const QList<PeerData*> &peers) {
|
||||
request(MTPchannels_GetChannels(MTP_vector<MTPInputChannel>(channels))).done(handleChats).send();
|
||||
}
|
||||
if (!users.isEmpty()) {
|
||||
request(MTPusers_GetUsers(MTP_vector<MTPInputUser>(users))).done([this](const MTPVector<MTPUser> &result) {
|
||||
request(MTPusers_GetUsers(MTP_vector<MTPInputUser>(users))).done([=](const MTPVector<MTPUser> &result) {
|
||||
App::feedUsers(result);
|
||||
}).send();
|
||||
}
|
||||
@@ -1509,16 +1509,28 @@ void ApiWrap::stickerSetDisenabled(mtpRequestId requestId) {
|
||||
}
|
||||
};
|
||||
|
||||
void ApiWrap::joinChannel(ChannelData *channel) {
|
||||
void ApiWrap::joinChannel(not_null<ChannelData*> channel) {
|
||||
if (channel->amIn()) {
|
||||
Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelAmIn);
|
||||
Notify::peerUpdatedDelayed(
|
||||
channel,
|
||||
Notify::PeerUpdate::Flag::ChannelAmIn);
|
||||
} else if (!_channelAmInRequests.contains(channel)) {
|
||||
auto requestId = request(MTPchannels_JoinChannel(channel->inputChannel)).done([this, channel](const MTPUpdates &result) {
|
||||
auto requestId = request(MTPchannels_JoinChannel(
|
||||
channel->inputChannel
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_channelAmInRequests.remove(channel);
|
||||
applyUpdates(result);
|
||||
}).fail([this, channel](const RPCError &error) {
|
||||
if (error.type() == qstr("CHANNELS_TOO_MUCH")) {
|
||||
}).fail([=](const RPCError &error) {
|
||||
if (error.type() == qstr("CHANNEL_PRIVATE")
|
||||
|| error.type() == qstr("CHANNEL_PUBLIC_GROUP_NA")
|
||||
|| error.type() == qstr("USER_BANNED_IN_CHANNEL")) {
|
||||
Ui::show(Box<InformBox>(lang(channel->isMegagroup()
|
||||
? lng_group_not_accessible
|
||||
: lng_channel_not_accessible)));
|
||||
} else if (error.type() == qstr("CHANNELS_TOO_MUCH")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_join_channel_error)));
|
||||
} else if (error.type() == qstr("USERS_TOO_MUCH")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_group_full)));
|
||||
}
|
||||
_channelAmInRequests.remove(channel);
|
||||
}).send();
|
||||
@@ -1527,14 +1539,18 @@ void ApiWrap::joinChannel(ChannelData *channel) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::leaveChannel(ChannelData *channel) {
|
||||
void ApiWrap::leaveChannel(not_null<ChannelData*> channel) {
|
||||
if (!channel->amIn()) {
|
||||
Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelAmIn);
|
||||
Notify::peerUpdatedDelayed(
|
||||
channel,
|
||||
Notify::PeerUpdate::Flag::ChannelAmIn);
|
||||
} else if (!_channelAmInRequests.contains(channel)) {
|
||||
auto requestId = request(MTPchannels_LeaveChannel(channel->inputChannel)).done([this, channel](const MTPUpdates &result) {
|
||||
auto requestId = request(MTPchannels_LeaveChannel(
|
||||
channel->inputChannel
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
_channelAmInRequests.remove(channel);
|
||||
applyUpdates(result);
|
||||
}).fail([this, channel](const RPCError &error) {
|
||||
}).fail([=](const RPCError &error) {
|
||||
_channelAmInRequests.remove(channel);
|
||||
}).send();
|
||||
|
||||
@@ -1607,7 +1623,10 @@ void ApiWrap::requestNotifySetting(PeerData *peer) {
|
||||
notifySettingReceived(notifyPeer, result);
|
||||
_notifySettingRequests.remove(peer);
|
||||
}).fail([this, notifyPeer, peer](const RPCError &error) {
|
||||
notifySettingReceived(notifyPeer, MTP_peerNotifySettingsEmpty());
|
||||
notifySettingReceived(notifyPeer, MTP_peerNotifySettings(
|
||||
MTP_flags(MTPDpeerNotifySettings::Flag::f_show_previews),
|
||||
MTP_int(0),
|
||||
MTP_string("default")));
|
||||
_notifySettingRequests.remove(peer);
|
||||
}).send();
|
||||
|
||||
@@ -1686,7 +1705,7 @@ void ApiWrap::handlePrivacyChange(mtpTypeId keyTypeId, const MTPVector<MTPPrivac
|
||||
}
|
||||
|
||||
auto now = unixtime();
|
||||
App::enumerateUsers([&userRules, contactsRule, everyoneRule, now](UserData *user) {
|
||||
App::enumerateUsers([&](UserData *user) {
|
||||
if (user->isSelf() || user->loadedStatus != PeerData::FullLoaded) {
|
||||
return;
|
||||
}
|
||||
@@ -2017,7 +2036,7 @@ void ApiWrap::requestParticipantsCountDelayed(
|
||||
not_null<ChannelData*> channel) {
|
||||
_participantsCountRequestTimer.call(
|
||||
kReloadChannelMembersTimeout,
|
||||
[this, channel] { channel->updateFullForced(); });
|
||||
[=] { channel->updateFullForced(); });
|
||||
}
|
||||
|
||||
void ApiWrap::requestChannelRangeDifference(not_null<History*> history) {
|
||||
@@ -2280,7 +2299,7 @@ void ApiWrap::requestStickers(TimeId now) {
|
||||
};
|
||||
_stickersUpdateRequest = request(MTPmessages_GetAllStickers(
|
||||
MTP_int(Local::countStickersHash(true))
|
||||
)).done(onDone).fail([this, onDone](const RPCError &error) {
|
||||
)).done(onDone).fail([=](const RPCError &error) {
|
||||
LOG(("App Fail: Failed to get stickers!"));
|
||||
onDone(MTP_messages_allStickersNotModified());
|
||||
}).send();
|
||||
@@ -3568,6 +3587,12 @@ void ApiWrap::sendSharedContact(
|
||||
MTP_string(firstName),
|
||||
MTP_string(lastName));
|
||||
sendMedia(item, media, peer->notifySilentPosts());
|
||||
|
||||
if (const auto main = App::main()) {
|
||||
_session->data().sendHistoryChangeNotifications();
|
||||
main->historyToDown(history);
|
||||
main->dialogsToUp();
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::sendVoiceMessage(
|
||||
@@ -3716,7 +3741,7 @@ void ApiWrap::uploadAlbumMedia(
|
||||
const MessageGroupId &groupId,
|
||||
const MTPInputMedia &media) {
|
||||
const auto localId = item->fullId();
|
||||
const auto failed = [this] {
|
||||
const auto failed = [=] {
|
||||
|
||||
};
|
||||
request(MTPmessages_UploadMedia(
|
||||
@@ -3726,6 +3751,7 @@ void ApiWrap::uploadAlbumMedia(
|
||||
const auto item = App::histItemById(localId);
|
||||
if (!item) {
|
||||
failed();
|
||||
return;
|
||||
}
|
||||
if (const auto media = item->media()) {
|
||||
if (const auto photo = media->photo()) {
|
||||
@@ -3870,7 +3896,7 @@ void ApiWrap::sendAlbumWithCancelled(
|
||||
const MessageGroupId &groupId) {
|
||||
const auto localId = item->fullId();
|
||||
const auto albumIt = _sendingAlbums.find(groupId.raw());
|
||||
if (albumIt != _sendingAlbums.end()) {
|
||||
if (albumIt == _sendingAlbums.end()) {
|
||||
// Sometimes we destroy item being sent already after the album
|
||||
// was sent successfully. For example the message could be loaded
|
||||
// from server (by messages.getHistory or updateNewMessage) and
|
||||
|
||||
@@ -23,7 +23,7 @@ struct SendingAlbum;
|
||||
enum class SendMediaType;
|
||||
|
||||
namespace Storage {
|
||||
enum class SharedMediaType : char;
|
||||
enum class SharedMediaType : signed char;
|
||||
struct PreparedList;
|
||||
} // namespace Storage
|
||||
|
||||
@@ -133,8 +133,8 @@ public:
|
||||
std::vector<not_null<DocumentData*>> *stickersByEmoji(
|
||||
not_null<EmojiPtr> emoji);
|
||||
|
||||
void joinChannel(ChannelData *channel);
|
||||
void leaveChannel(ChannelData *channel);
|
||||
void joinChannel(not_null<ChannelData*> channel);
|
||||
void leaveChannel(not_null<ChannelData*> channel);
|
||||
|
||||
void blockUser(UserData *user);
|
||||
void unblockUser(UserData *user);
|
||||
|
||||
@@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
@@ -167,9 +168,6 @@ namespace {
|
||||
|
||||
cSetOtherOnline(0);
|
||||
clearStorageImages();
|
||||
if (auto w = wnd()) {
|
||||
w->updateConnectingStatus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
@@ -923,6 +921,7 @@ namespace {
|
||||
: nullptr);
|
||||
existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
|
||||
existing->indexAsNewItem();
|
||||
Auth().data().requestItemTextRefresh(existing);
|
||||
if (existing->mainView()) {
|
||||
App::checkSavedGif(existing);
|
||||
return true;
|
||||
@@ -1627,6 +1626,7 @@ namespace {
|
||||
int64 nowImageCacheSize = imageCacheSize();
|
||||
if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) {
|
||||
App::forgetMedia();
|
||||
Auth().data().forgetMedia();
|
||||
serviceImageCacheSize = imageCacheSize();
|
||||
}
|
||||
}
|
||||
@@ -1665,7 +1665,7 @@ namespace {
|
||||
|
||||
void restart() {
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady);
|
||||
bool updateReady = (Core::UpdateChecker().state() == Core::UpdateChecker::State::Ready);
|
||||
#else // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
bool updateReady = false;
|
||||
#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
@@ -1780,38 +1780,6 @@ namespace {
|
||||
if (changeInMin) App::main()->updateMutedIn(changeInMin);
|
||||
}
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager) {
|
||||
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
manager.setProxy(getHttpProxySettings());
|
||||
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
QNetworkProxy getHttpProxySettings() {
|
||||
const ProxyData *proxy = nullptr;
|
||||
if (Global::started()) {
|
||||
proxy = (Global::ConnectionType() == dbictHttpProxy) ? (&Global::ConnectionProxy()) : nullptr;
|
||||
} else {
|
||||
proxy = Sandbox::PreLaunchProxy().host.isEmpty() ? nullptr : (&Sandbox::PreLaunchProxy());
|
||||
}
|
||||
if (proxy) {
|
||||
return QNetworkProxy(QNetworkProxy::HttpProxy, proxy->host, proxy->port, proxy->user, proxy->password);
|
||||
}
|
||||
return QNetworkProxy(QNetworkProxy::DefaultProxy);
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
|
||||
void setProxySettings(QTcpSocket &socket) {
|
||||
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
if (Global::ConnectionType() == dbictTcpProxy) {
|
||||
auto &p = Global::ConnectionProxy();
|
||||
socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, p.host, p.port, p.user, p.password));
|
||||
} else {
|
||||
socket.setProxy(QNetworkProxy(QNetworkProxy::NoProxy));
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
}
|
||||
|
||||
void rectWithCorners(Painter &p, QRect rect, const style::color &bg, RoundCorners index, RectParts corners) {
|
||||
auto parts = RectPart::Top
|
||||
| RectPart::NoTopBottom
|
||||
|
||||
@@ -225,12 +225,6 @@ namespace App {
|
||||
void unregMuted(not_null<PeerData*> peer);
|
||||
void updateMuted();
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager);
|
||||
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
QNetworkProxy getHttpProxySettings();
|
||||
#endif // !TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
void setProxySettings(QTcpSocket &socket);
|
||||
|
||||
void complexOverlayRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
|
||||
void complexLocationRect(Painter &p, QRect rect, ImageRoundRadius radius, RectParts corners);
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "autoupdater.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "messenger.h"
|
||||
#include "base/timer.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/crash_report_window.h"
|
||||
|
||||
namespace {
|
||||
@@ -65,7 +65,11 @@ Application::Application(
|
||||
int &argc,
|
||||
char **argv)
|
||||
: QApplication(argc, argv)
|
||||
, _launcher(launcher) {
|
||||
, _launcher(launcher)
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
, _updateChecker(std::make_unique<Core::UpdateChecker>())
|
||||
#endif // TDESKTOP_DISABLE_AUTOUPDATE
|
||||
{
|
||||
const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
|
||||
char h[33] = { 0 };
|
||||
hashMd5Hex(d.constData(), d.size(), h);
|
||||
@@ -86,13 +90,6 @@ Application::Application(
|
||||
QTimer::singleShot(0, this, SLOT(startApplication()));
|
||||
connect(this, SIGNAL(aboutToQuit()), this, SLOT(closeApplication()));
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
_updateCheckTimer.create(this);
|
||||
connect(_updateCheckTimer, SIGNAL(timeout()), this, SLOT(updateCheck()));
|
||||
connect(this, SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
|
||||
connect(this, SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
if (cManyInstance()) {
|
||||
LOG(("Many instance allowed, starting..."));
|
||||
singleInstanceChecked();
|
||||
@@ -180,7 +177,7 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
|
||||
#endif // !Q_OS_WINRT
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
if (!cNoStartUpdate() && checkReadyUpdate()) {
|
||||
if (!cNoStartUpdate() && Core::checkReadyUpdate()) {
|
||||
cSetRestartingUpdate(true);
|
||||
DEBUG_LOG(("Application Info: installing update instead of starting app..."));
|
||||
return App::quit();
|
||||
@@ -196,6 +193,7 @@ void Application::singleInstanceChecked() {
|
||||
}
|
||||
|
||||
Sandbox::start();
|
||||
refreshGlobalProxy();
|
||||
|
||||
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
|
||||
new NotStartedWindow();
|
||||
@@ -307,9 +305,29 @@ void Application::startApplication() {
|
||||
|
||||
void Application::createMessenger() {
|
||||
Expects(!App::quitting());
|
||||
|
||||
_messengerInstance = std::make_unique<Messenger>(_launcher);
|
||||
}
|
||||
|
||||
void Application::refreshGlobalProxy() {
|
||||
#ifndef TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
const auto proxy = [&] {
|
||||
if (Global::started()) {
|
||||
return Global::UseProxy()
|
||||
? Global::SelectedProxy()
|
||||
: ProxyData();
|
||||
}
|
||||
return Sandbox::PreLaunchProxy();
|
||||
}();
|
||||
if (proxy.type == ProxyData::Type::Socks5
|
||||
|| proxy.type == ProxyData::Type::Http) {
|
||||
QNetworkProxy::setApplicationProxy(ToNetworkProxy(proxy));
|
||||
} else {
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
}
|
||||
#endif // TDESKTOP_DISABLE_NETWORK_PROXY
|
||||
}
|
||||
|
||||
void Application::closeApplication() {
|
||||
if (App::launchState() == App::QuitProcessed) return;
|
||||
App::setLaunchState(App::QuitProcessed);
|
||||
@@ -328,169 +346,10 @@ void Application::closeApplication() {
|
||||
_localSocket.close();
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
delete _updateReply;
|
||||
_updateReply = 0;
|
||||
if (_updateChecker) _updateChecker->deleteLater();
|
||||
_updateChecker = 0;
|
||||
if (_updateThread) {
|
||||
_updateThread->quit();
|
||||
}
|
||||
_updateThread = 0;
|
||||
_updateChecker = nullptr;
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
void Application::updateCheck() {
|
||||
startUpdateCheck(false);
|
||||
}
|
||||
|
||||
void Application::updateGotCurrent() {
|
||||
if (!_updateReply || _updateThread) return;
|
||||
|
||||
cSetLastUpdateCheck(unixtime());
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromLatin1(_updateReply->readAll()));
|
||||
if (m.hasMatch()) {
|
||||
uint64 currentVersion = m.captured(1).toULongLong();
|
||||
QString url = m.captured(2);
|
||||
bool betaVersion = false;
|
||||
if (url.startsWith(qstr("beta_"))) {
|
||||
betaVersion = true;
|
||||
url = url.mid(5) + '_' + countBetaVersionSignature(currentVersion);
|
||||
}
|
||||
if ((!betaVersion || cBetaVersion()) && currentVersion > (betaVersion ? cBetaVersion() : uint64(AppVersion))) {
|
||||
_updateThread = new QThread();
|
||||
connect(_updateThread, SIGNAL(finished()), _updateThread, SLOT(deleteLater()));
|
||||
_updateChecker = new UpdateChecker(_updateThread, url);
|
||||
_updateThread->start();
|
||||
}
|
||||
}
|
||||
if (_updateReply) _updateReply->deleteLater();
|
||||
_updateReply = 0;
|
||||
if (!_updateThread) {
|
||||
QDir updates(cWorkingDir() + "tupdates");
|
||||
if (updates.exists()) {
|
||||
QFileInfoList list = updates.entryInfoList(QDir::Files);
|
||||
for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
|
||||
if (QRegularExpression("^(tupdate|tmacupd|tmac32upd|tlinuxupd|tlinux32upd)\\d+(_[a-z\\d]+)?$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
|
||||
QFile(i->absoluteFilePath()).remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
emit updateLatest();
|
||||
}
|
||||
startUpdateCheck(true);
|
||||
Local::writeSettings();
|
||||
}
|
||||
|
||||
void Application::updateFailedCurrent(QNetworkReply::NetworkError e) {
|
||||
LOG(("App Error: could not get current version (update check): %1").arg(e));
|
||||
if (_updateReply) _updateReply->deleteLater();
|
||||
_updateReply = 0;
|
||||
|
||||
emit updateFailed();
|
||||
startUpdateCheck(true);
|
||||
}
|
||||
|
||||
void Application::onUpdateReady() {
|
||||
if (_updateChecker) {
|
||||
_updateChecker->deleteLater();
|
||||
_updateChecker = nullptr;
|
||||
}
|
||||
_updateCheckTimer->stop();
|
||||
|
||||
cSetLastUpdateCheck(unixtime());
|
||||
Local::writeSettings();
|
||||
}
|
||||
|
||||
void Application::onUpdateFailed() {
|
||||
if (_updateChecker) {
|
||||
_updateChecker->deleteLater();
|
||||
_updateChecker = 0;
|
||||
if (_updateThread) _updateThread->quit();
|
||||
_updateThread = 0;
|
||||
}
|
||||
|
||||
cSetLastUpdateCheck(unixtime());
|
||||
Local::writeSettings();
|
||||
}
|
||||
|
||||
Application::UpdatingState Application::updatingState() {
|
||||
if (!_updateThread) return Application::UpdatingNone;
|
||||
if (!_updateChecker) return Application::UpdatingReady;
|
||||
return Application::UpdatingDownload;
|
||||
}
|
||||
|
||||
int32 Application::updatingSize() {
|
||||
if (!_updateChecker) return 0;
|
||||
return _updateChecker->size();
|
||||
}
|
||||
|
||||
int32 Application::updatingReady() {
|
||||
if (!_updateChecker) return 0;
|
||||
return _updateChecker->ready();
|
||||
}
|
||||
|
||||
void Application::stopUpdate() {
|
||||
if (_updateReply) {
|
||||
_updateReply->abort();
|
||||
_updateReply->deleteLater();
|
||||
_updateReply = 0;
|
||||
}
|
||||
if (_updateChecker) {
|
||||
_updateChecker->deleteLater();
|
||||
_updateChecker = 0;
|
||||
if (_updateThread) _updateThread->quit();
|
||||
_updateThread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::startUpdateCheck(bool forceWait) {
|
||||
if (!Sandbox::started()) return;
|
||||
|
||||
_updateCheckTimer->stop();
|
||||
if (_updateThread || _updateReply || !cAutoUpdate() || cExeName().isEmpty()) return;
|
||||
|
||||
int32 constDelay = cBetaVersion() ? 600 : UpdateDelayConstPart, randDelay = cBetaVersion() ? 300 : UpdateDelayRandPart;
|
||||
int32 updateInSecs = cLastUpdateCheck() + constDelay + int32(rand() % randDelay) - unixtime();
|
||||
bool sendRequest = (updateInSecs <= 0 || updateInSecs > (constDelay + randDelay));
|
||||
if (!sendRequest && !forceWait) {
|
||||
QDir updates(cWorkingDir() + "tupdates");
|
||||
if (updates.exists()) {
|
||||
QFileInfoList list = updates.entryInfoList(QDir::Files);
|
||||
for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
|
||||
if (QRegularExpression("^(tupdate|tmacupd|tmac32upd|tlinuxupd|tlinux32upd)\\d+(_[a-z\\d]+)?$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
|
||||
sendRequest = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cManyInstance() && !cDebug()) return; // only main instance is updating
|
||||
|
||||
if (sendRequest) {
|
||||
QUrl url(cUpdateURL());
|
||||
if (cBetaVersion()) {
|
||||
url.setQuery(qsl("version=%1&beta=%2").arg(AppVersion).arg(cBetaVersion()));
|
||||
} else if (cAlphaVersion()) {
|
||||
url.setQuery(qsl("version=%1&alpha=1").arg(AppVersion));
|
||||
} else {
|
||||
url.setQuery(qsl("version=%1").arg(AppVersion));
|
||||
}
|
||||
QString u = url.toString();
|
||||
QNetworkRequest checkVersion(url);
|
||||
if (_updateReply) _updateReply->deleteLater();
|
||||
|
||||
App::setProxySettings(_updateManager);
|
||||
_updateReply = _updateManager.get(checkVersion);
|
||||
connect(_updateReply, SIGNAL(finished()), this, SLOT(updateGotCurrent()));
|
||||
connect(_updateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(updateFailedCurrent(QNetworkReply::NetworkError)));
|
||||
emit updateChecking();
|
||||
} else {
|
||||
_updateCheckTimer->start((updateInSecs + 5) * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
inline Application *application() {
|
||||
return qobject_cast<Application*>(QApplication::instance());
|
||||
}
|
||||
@@ -542,73 +401,6 @@ void adjustSingleTimers() {
|
||||
base::Timer::Adjust();
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
void startUpdateCheck() {
|
||||
if (auto a = application()) {
|
||||
return a->startUpdateCheck(false);
|
||||
}
|
||||
}
|
||||
|
||||
void stopUpdate() {
|
||||
if (auto a = application()) {
|
||||
return a->stopUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
Application::UpdatingState updatingState() {
|
||||
if (auto a = application()) {
|
||||
return a->updatingState();
|
||||
}
|
||||
return Application::UpdatingNone;
|
||||
}
|
||||
|
||||
int32 updatingSize() {
|
||||
if (auto a = application()) {
|
||||
return a->updatingSize();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32 updatingReady() {
|
||||
if (auto a = application()) {
|
||||
return a->updatingReady();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void updateChecking() {
|
||||
if (auto a = application()) {
|
||||
emit a->updateChecking();
|
||||
}
|
||||
}
|
||||
|
||||
void updateLatest() {
|
||||
if (auto a = application()) {
|
||||
emit a->updateLatest();
|
||||
}
|
||||
}
|
||||
|
||||
void updateProgress(qint64 ready, qint64 total) {
|
||||
if (auto a = application()) {
|
||||
emit a->updateProgress(ready, total);
|
||||
}
|
||||
}
|
||||
|
||||
void updateFailed() {
|
||||
if (auto a = application()) {
|
||||
emit a->updateFailed();
|
||||
}
|
||||
}
|
||||
|
||||
void updateReady() {
|
||||
if (auto a = application()) {
|
||||
emit a->updateReady();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
void connect(const char *signal, QObject *object, const char *method) {
|
||||
if (auto a = application()) {
|
||||
a->connect(a, signal, object, method);
|
||||
@@ -648,4 +440,10 @@ void launch() {
|
||||
application()->createMessenger();
|
||||
}
|
||||
|
||||
void refreshGlobalProxy() {
|
||||
if (const auto instance = application()) {
|
||||
instance->refreshGlobalProxy();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Sandbox
|
||||
|
||||
@@ -7,10 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class UpdateChecker;
|
||||
|
||||
namespace Core {
|
||||
class Launcher;
|
||||
class UpdateChecker;
|
||||
} // namespace Core
|
||||
|
||||
class Application : public QApplication {
|
||||
@@ -22,6 +21,7 @@ public:
|
||||
bool event(QEvent *e) override;
|
||||
|
||||
void createMessenger();
|
||||
void refreshGlobalProxy();
|
||||
|
||||
~Application();
|
||||
|
||||
@@ -58,46 +58,11 @@ private:
|
||||
|
||||
void singleInstanceChecked();
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
// Autoupdating
|
||||
public:
|
||||
void startUpdateCheck(bool forceWait);
|
||||
void stopUpdate();
|
||||
|
||||
enum UpdatingState {
|
||||
UpdatingNone,
|
||||
UpdatingDownload,
|
||||
UpdatingReady,
|
||||
};
|
||||
UpdatingState updatingState();
|
||||
int32 updatingSize();
|
||||
int32 updatingReady();
|
||||
|
||||
signals:
|
||||
void updateChecking();
|
||||
void updateLatest();
|
||||
void updateProgress(qint64 ready, qint64 total);
|
||||
void updateReady();
|
||||
void updateFailed();
|
||||
|
||||
public slots:
|
||||
void updateCheck();
|
||||
|
||||
void updateGotCurrent();
|
||||
void updateFailedCurrent(QNetworkReply::NetworkError e);
|
||||
|
||||
void onUpdateReady();
|
||||
void onUpdateFailed();
|
||||
|
||||
private:
|
||||
object_ptr<SingleTimer> _updateCheckTimer = { nullptr };
|
||||
QNetworkReply *_updateReply = nullptr;
|
||||
QNetworkAccessManager _updateManager;
|
||||
QThread *_updateThread = nullptr;
|
||||
UpdateChecker *_updateChecker = nullptr;
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
std::unique_ptr<Core::UpdateChecker> _updateChecker;
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
};
|
||||
|
||||
namespace Sandbox {
|
||||
@@ -111,22 +76,7 @@ void execExternal(const QString &cmd);
|
||||
|
||||
void adjustSingleTimers();
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
void startUpdateCheck();
|
||||
void stopUpdate();
|
||||
|
||||
Application::UpdatingState updatingState();
|
||||
int32 updatingSize();
|
||||
int32 updatingReady();
|
||||
|
||||
void updateChecking();
|
||||
void updateLatest();
|
||||
void updateProgress(qint64 ready, qint64 total);
|
||||
void updateFailed();
|
||||
void updateReady();
|
||||
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
void refreshGlobalProxy();
|
||||
|
||||
void connect(const char *signal, QObject *object, const char *method);
|
||||
|
||||
|
||||
@@ -278,10 +278,10 @@ AuthSession::AuthSession(UserId userId)
|
||||
, _changelogs(Core::Changelogs::Create(this)) {
|
||||
Expects(_userId != 0);
|
||||
|
||||
_saveDataTimer.setCallback([this] {
|
||||
_saveDataTimer.setCallback([=] {
|
||||
Local::writeUserSettings();
|
||||
});
|
||||
subscribe(Messenger::Instance().passcodedChanged(), [this] {
|
||||
subscribe(Messenger::Instance().passcodedChanged(), [=] {
|
||||
_shouldLockAt = 0;
|
||||
notifications().updateAll();
|
||||
});
|
||||
@@ -304,7 +304,10 @@ 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();
|
||||
return false;
|
||||
@@ -314,6 +317,7 @@ bool AuthSession::validateSelf(const MTPUser &user) {
|
||||
|
||||
void AuthSession::saveSettingsDelayed(TimeMs delay) {
|
||||
Expects(this == &Auth());
|
||||
|
||||
_saveDataTimer.callOnce(delay);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,614 +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
|
||||
*/
|
||||
|
||||
#include "autoupdater.h"
|
||||
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
#include <LzmaLib.h>
|
||||
#else // Q_OS_WIN
|
||||
#include <lzma.h>
|
||||
#endif // else of Q_OS_WIN
|
||||
|
||||
#include "application.h"
|
||||
#include "platform/platform_specific.h"
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
typedef DWORD VerInt;
|
||||
typedef WCHAR VerChar;
|
||||
#else // Q_OS_WIN
|
||||
typedef int VerInt;
|
||||
typedef wchar_t VerChar;
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
UpdateChecker::UpdateChecker(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
|
||||
updateUrl = url;
|
||||
moveToThread(thread);
|
||||
manager.moveToThread(thread);
|
||||
App::setProxySettings(manager);
|
||||
|
||||
connect(thread, SIGNAL(started()), this, SLOT(start()));
|
||||
initOutput();
|
||||
}
|
||||
|
||||
void UpdateChecker::initOutput() {
|
||||
QString fileName;
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
|
||||
if (m.hasMatch()) {
|
||||
fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
|
||||
}
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = qsl("tupdate-%1").arg(rand_value<uint32>() % 1000000);
|
||||
}
|
||||
QString dirStr = cWorkingDir() + qsl("tupdates/");
|
||||
fileName = dirStr + fileName;
|
||||
QFileInfo file(fileName);
|
||||
|
||||
QDir dir(dirStr);
|
||||
if (dir.exists()) {
|
||||
QFileInfoList all = dir.entryInfoList(QDir::Files);
|
||||
for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
|
||||
if (i->absoluteFilePath() != file.absoluteFilePath()) {
|
||||
QFile::remove(i->absoluteFilePath());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dir.mkdir(dir.absolutePath());
|
||||
}
|
||||
outputFile.setFileName(fileName);
|
||||
if (file.exists()) {
|
||||
uint64 fullSize = file.size();
|
||||
if (fullSize < INT_MAX) {
|
||||
int32 goodSize = (int32)fullSize;
|
||||
if (goodSize % UpdateChunk) {
|
||||
goodSize = goodSize - (goodSize % UpdateChunk);
|
||||
if (goodSize) {
|
||||
if (outputFile.open(QIODevice::ReadOnly)) {
|
||||
QByteArray goodData = outputFile.readAll().mid(0, goodSize);
|
||||
outputFile.close();
|
||||
if (outputFile.open(QIODevice::WriteOnly)) {
|
||||
outputFile.write(goodData);
|
||||
outputFile.close();
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
already = goodSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QMutexLocker lock(&mutex);
|
||||
already = goodSize;
|
||||
}
|
||||
}
|
||||
if (!already) {
|
||||
QFile::remove(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateChecker::start() {
|
||||
sendRequest();
|
||||
}
|
||||
|
||||
void UpdateChecker::sendRequest() {
|
||||
QNetworkRequest req(updateUrl);
|
||||
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";
|
||||
req.setRawHeader("Range", rangeHeaderValue);
|
||||
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
|
||||
if (reply) reply->deleteLater();
|
||||
reply = manager.get(req);
|
||||
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
|
||||
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
|
||||
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
|
||||
}
|
||||
|
||||
void UpdateChecker::partMetaGot() {
|
||||
typedef QList<QNetworkReply::RawHeaderPair> Pairs;
|
||||
Pairs pairs = reply->rawHeaderPairs();
|
||||
for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
|
||||
if (QString::fromUtf8(i->first).toLower() == "content-range") {
|
||||
QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
|
||||
if (m.hasMatch()) {
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
full = m.captured(1).toInt();
|
||||
}
|
||||
|
||||
Sandbox::updateProgress(already, full);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 UpdateChecker::ready() {
|
||||
QMutexLocker lock(&mutex);
|
||||
return already;
|
||||
}
|
||||
|
||||
int32 UpdateChecker::size() {
|
||||
QMutexLocker lock(&mutex);
|
||||
return full;
|
||||
}
|
||||
|
||||
void UpdateChecker::partFinished(qint64 got, qint64 total) {
|
||||
if (!reply) return;
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
if (statusCode.isValid()) {
|
||||
int status = statusCode.toInt();
|
||||
if (status != 200 && status != 206 && status != 416) {
|
||||
LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
|
||||
if (!already && !full) {
|
||||
QMutexLocker lock(&mutex);
|
||||
full = total;
|
||||
}
|
||||
DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
|
||||
|
||||
if (!outputFile.isOpen()) {
|
||||
if (!outputFile.open(QIODevice::Append)) {
|
||||
LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
QByteArray r = reply->readAll();
|
||||
if (!r.isEmpty()) {
|
||||
outputFile.write(r);
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
already += r.size();
|
||||
}
|
||||
if (got >= total) {
|
||||
reply->deleteLater();
|
||||
reply = 0;
|
||||
outputFile.close();
|
||||
unpackUpdate();
|
||||
} else {
|
||||
Sandbox::updateProgress(already, full);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateChecker::partFailed(QNetworkReply::NetworkError e) {
|
||||
if (!reply) return;
|
||||
|
||||
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
||||
reply->deleteLater();
|
||||
reply = 0;
|
||||
if (statusCode.isValid()) {
|
||||
int status = statusCode.toInt();
|
||||
if (status == 416) { // Requested range not satisfiable
|
||||
outputFile.close();
|
||||
unpackUpdate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
|
||||
Sandbox::updateFailed();
|
||||
}
|
||||
|
||||
void UpdateChecker::fatalFail() {
|
||||
clearAll();
|
||||
Sandbox::updateFailed();
|
||||
}
|
||||
|
||||
void UpdateChecker::clearAll() {
|
||||
psDeleteDir(cWorkingDir() + qsl("tupdates"));
|
||||
}
|
||||
|
||||
//QString winapiErrorWrap() {
|
||||
// WCHAR errMsg[2048];
|
||||
// DWORD errorCode = GetLastError();
|
||||
// LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
|
||||
// FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, 0, 0);
|
||||
// if (!errorText) {
|
||||
// errorText = errorTextDefault;
|
||||
// }
|
||||
// StringCbPrintf(errMsg, sizeof(errMsg), L"Error code: %d, error message: %s", errorCode, errorText);
|
||||
// if (errorText != errorTextDefault) {
|
||||
// LocalFree(errorText);
|
||||
// }
|
||||
// return QString::fromWCharArray(errMsg);
|
||||
//}
|
||||
|
||||
void UpdateChecker::unpackUpdate() {
|
||||
QByteArray packed;
|
||||
if (!outputFile.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Update Error: cant read updates file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
|
||||
#else // Q_OS_WIN
|
||||
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
QByteArray compressed = outputFile.readAll();
|
||||
int32 compressedLen = compressed.size() - hSize;
|
||||
if (compressedLen <= 0) {
|
||||
LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
|
||||
return fatalFail();
|
||||
}
|
||||
outputFile.close();
|
||||
|
||||
QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready");
|
||||
psDeleteDir(tempDirPath);
|
||||
|
||||
QDir tempDir(tempDirPath);
|
||||
if (tempDir.exists() || QFile(readyFilePath).exists()) {
|
||||
LOG(("Update Error: cant clear tupdates/temp dir!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
uchar sha1Buffer[20];
|
||||
bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
|
||||
if (!goodSha1) {
|
||||
LOG(("Update Error: bad SHA1 hash of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(AppAlphaVersion ? UpdatesPublicAlphaKey : UpdatesPublicKey), -1), 0, 0, 0);
|
||||
if (!pbKey) {
|
||||
LOG(("Update Error: cant read public rsa key!"));
|
||||
return fatalFail();
|
||||
}
|
||||
if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
|
||||
RSA_free(pbKey);
|
||||
if (cAlphaVersion() || cBetaVersion()) { // try other public key, if we are in alpha or beta version
|
||||
pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(AppAlphaVersion ? UpdatesPublicKey : UpdatesPublicAlphaKey), -1), 0, 0, 0);
|
||||
if (!pbKey) {
|
||||
LOG(("Update Error: cant read public rsa key!"));
|
||||
return fatalFail();
|
||||
}
|
||||
if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
|
||||
RSA_free(pbKey);
|
||||
LOG(("Update Error: bad RSA signature of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
} else {
|
||||
LOG(("Update Error: bad RSA signature of update file!"));
|
||||
return fatalFail();
|
||||
}
|
||||
}
|
||||
RSA_free(pbKey);
|
||||
|
||||
QByteArray uncompressed;
|
||||
|
||||
int32 uncompressedLen;
|
||||
memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
|
||||
uncompressed.resize(uncompressedLen);
|
||||
|
||||
size_t resultLen = uncompressed.size();
|
||||
#ifdef Q_OS_WIN // use Lzma SDK for win
|
||||
SizeT srcLen = compressedLen;
|
||||
int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
|
||||
if (uncompressRes != SZ_OK) {
|
||||
LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
|
||||
return fatalFail();
|
||||
}
|
||||
#else // Q_OS_WIN
|
||||
lzma_stream stream = LZMA_STREAM_INIT;
|
||||
|
||||
lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
|
||||
if (ret != LZMA_OK) {
|
||||
const char *msg;
|
||||
switch (ret) {
|
||||
case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
|
||||
case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
|
||||
case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
|
||||
default: msg = "Unknown error, possibly a bug"; break;
|
||||
}
|
||||
LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
stream.avail_in = compressedLen;
|
||||
stream.next_in = (uint8_t*)(compressed.constData() + hSize);
|
||||
stream.avail_out = resultLen;
|
||||
stream.next_out = (uint8_t*)uncompressed.data();
|
||||
|
||||
lzma_ret res = lzma_code(&stream, LZMA_FINISH);
|
||||
if (stream.avail_in) {
|
||||
LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
|
||||
return fatalFail();
|
||||
} else if (stream.avail_out) {
|
||||
LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
|
||||
return fatalFail();
|
||||
}
|
||||
lzma_end(&stream);
|
||||
if (res != LZMA_OK && res != LZMA_STREAM_END) {
|
||||
const char *msg;
|
||||
switch (res) {
|
||||
case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
|
||||
case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
|
||||
case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
|
||||
case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
|
||||
case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
|
||||
default: msg = "Unknown error, possibly a bug"; break;
|
||||
}
|
||||
LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
|
||||
return fatalFail();
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
tempDir.mkdir(tempDir.absolutePath());
|
||||
|
||||
quint32 version;
|
||||
{
|
||||
QDataStream stream(uncompressed);
|
||||
stream.setVersion(QDataStream::Qt_5_1);
|
||||
|
||||
stream >> version;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
quint64 betaVersion = 0;
|
||||
if (version == 0x7FFFFFFF) { // beta version
|
||||
stream >> betaVersion;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read beta version from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!cBetaVersion() || betaVersion <= cBetaVersion()) {
|
||||
LOG(("Update Error: downloaded beta version %1 is not greater, than mine %2").arg(betaVersion).arg(cBetaVersion()));
|
||||
return fatalFail();
|
||||
}
|
||||
} else if (int32(version) <= AppVersion) {
|
||||
LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
quint32 filesCount;
|
||||
stream >> filesCount;
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!filesCount) {
|
||||
LOG(("Update Error: update is empty!"));
|
||||
return fatalFail();
|
||||
}
|
||||
for (uint32 i = 0; i < filesCount; ++i) {
|
||||
QString relativeName;
|
||||
quint32 fileSize;
|
||||
QByteArray fileInnerData;
|
||||
bool executable = false;
|
||||
|
||||
stream >> relativeName >> fileSize >> fileInnerData;
|
||||
#if defined Q_OS_MAC || defined Q_OS_LINUX
|
||||
stream >> executable;
|
||||
#endif // Q_OS_MAC || Q_OS_LINUX
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
|
||||
return fatalFail();
|
||||
}
|
||||
if (fileSize != quint32(fileInnerData.size())) {
|
||||
LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
|
||||
return fatalFail();
|
||||
}
|
||||
|
||||
QFile f(tempDirPath + '/' + relativeName);
|
||||
if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
|
||||
LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
if (!f.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
|
||||
return fatalFail();
|
||||
}
|
||||
auto writtenBytes = f.write(fileInnerData);
|
||||
if (writtenBytes != fileSize) {
|
||||
f.close();
|
||||
LOG(("Update Error: cant write file '%1', desiredSize: %2, write result: %3").arg(tempDirPath + '/' + relativeName).arg(fileSize).arg(writtenBytes));
|
||||
return fatalFail();
|
||||
}
|
||||
f.close();
|
||||
if (executable) {
|
||||
QFileDevice::Permissions p = f.permissions();
|
||||
p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
|
||||
f.setPermissions(p);
|
||||
}
|
||||
}
|
||||
|
||||
// create tdata/version file
|
||||
tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
|
||||
std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
|
||||
|
||||
VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
|
||||
VerChar versionStr[32];
|
||||
memcpy(versionStr, versionString.c_str(), versionLen);
|
||||
|
||||
QFile fVersion(tempDirPath + qsl("/tdata/version"));
|
||||
if (!fVersion.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
|
||||
return fatalFail();
|
||||
}
|
||||
fVersion.write((const char*)&versionNum, sizeof(VerInt));
|
||||
if (versionNum == 0x7FFFFFFF) { // beta version
|
||||
fVersion.write((const char*)&betaVersion, sizeof(quint64));
|
||||
} else {
|
||||
fVersion.write((const char*)&versionLen, sizeof(VerInt));
|
||||
fVersion.write((const char*)&versionStr[0], versionLen);
|
||||
}
|
||||
fVersion.close();
|
||||
}
|
||||
|
||||
QFile readyFile(readyFilePath);
|
||||
if (readyFile.open(QIODevice::WriteOnly)) {
|
||||
if (readyFile.write("1", 1)) {
|
||||
readyFile.close();
|
||||
} else {
|
||||
LOG(("Update Error: cant write ready file '%1'").arg(readyFilePath));
|
||||
return fatalFail();
|
||||
}
|
||||
} else {
|
||||
LOG(("Update Error: cant create ready file '%1'").arg(readyFilePath));
|
||||
return fatalFail();
|
||||
}
|
||||
outputFile.remove();
|
||||
|
||||
Sandbox::updateReady();
|
||||
}
|
||||
|
||||
UpdateChecker::~UpdateChecker() {
|
||||
delete reply;
|
||||
reply = 0;
|
||||
}
|
||||
|
||||
bool checkReadyUpdate() {
|
||||
QString readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready"), readyPath = cWorkingDir() + qsl("tupdates/temp");
|
||||
if (!QFile(readyFilePath).exists() || cExeName().isEmpty()) {
|
||||
if (QDir(cWorkingDir() + qsl("tupdates/ready")).exists() || QDir(cWorkingDir() + qsl("tupdates/temp")).exists()) {
|
||||
UpdateChecker::clearAll();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// check ready version
|
||||
QString versionPath = readyPath + qsl("/tdata/version");
|
||||
{
|
||||
QFile fVersion(versionPath);
|
||||
if (!fVersion.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Update Error: cant read version file '%1'").arg(versionPath));
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
VerInt versionNum;
|
||||
if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
|
||||
LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
if (versionNum == 0x7FFFFFFF) { // beta version
|
||||
quint64 betaVersion = 0;
|
||||
if (fVersion.read((char*)&betaVersion, sizeof(quint64)) != sizeof(quint64)) {
|
||||
LOG(("Update Error: cant read beta version from file '%1'").arg(versionPath));
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
if (!cBetaVersion() || betaVersion <= cBetaVersion()) {
|
||||
LOG(("Update Error: cant install beta version %1 having beta version %2").arg(betaVersion).arg(cBetaVersion()));
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
} else if (versionNum <= AppVersion) {
|
||||
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
fVersion.close();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QString curUpdater = (cExeDir() + qsl("Updater.exe"));
|
||||
QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Updater.exe"));
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
QString curUpdater = (cExeDir() + cExeName() + qsl("/Contents/Frameworks/Updater"));
|
||||
QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Telegram.app/Contents/Frameworks/Updater"));
|
||||
#elif defined Q_OS_LINUX // Q_OS_MAC
|
||||
QString curUpdater = (cExeDir() + qsl("Updater"));
|
||||
QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Updater"));
|
||||
#endif // Q_OS_LINUX
|
||||
if (!updater.exists()) {
|
||||
QFileInfo current(curUpdater);
|
||||
if (!current.exists()) {
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef Q_OS_WIN
|
||||
if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
|
||||
DWORD errorCode = GetLastError();
|
||||
if (errorCode == ERROR_ACCESS_DENIED) { // we are in write-protected dir, like Program Files
|
||||
cSetWriteProtected(true);
|
||||
return true;
|
||||
} else {
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
#elif defined Q_OS_MAC // Q_OS_WIN
|
||||
QDir().mkpath(QFileInfo(curUpdater).absolutePath());
|
||||
DEBUG_LOG(("Update Info: moving %1 to %2...").arg(updater.absoluteFilePath()).arg(curUpdater));
|
||||
if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
#elif defined Q_OS_LINUX // Q_OS_MAC
|
||||
if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
|
||||
UpdateChecker::clearAll();
|
||||
return false;
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
QString countBetaVersionSignature(uint64 version) { // duplicated in packer.cpp
|
||||
if (cBetaPrivateKey().isEmpty()) {
|
||||
LOG(("Error: Trying to count beta version signature without beta private key!"));
|
||||
return QString();
|
||||
}
|
||||
|
||||
QByteArray signedData = (qstr("TelegramBeta_") + QString::number(version, 16).toLower()).toUtf8();
|
||||
|
||||
static const int32 shaSize = 20, keySize = 128;
|
||||
|
||||
uchar sha1Buffer[shaSize];
|
||||
hashSha1(signedData.constData(), signedData.size(), sha1Buffer); // count sha1
|
||||
|
||||
uint32 siglen = 0;
|
||||
|
||||
RSA *prKey = PEM_read_bio_RSAPrivateKey(BIO_new_mem_buf(const_cast<char*>(cBetaPrivateKey().constData()), -1), 0, 0, 0);
|
||||
if (!prKey) {
|
||||
LOG(("Error: Could not read beta private key!"));
|
||||
return QString();
|
||||
}
|
||||
if (RSA_size(prKey) != keySize) {
|
||||
LOG(("Error: Bad beta private key size: %1").arg(RSA_size(prKey)));
|
||||
RSA_free(prKey);
|
||||
return QString();
|
||||
}
|
||||
QByteArray signature;
|
||||
signature.resize(keySize);
|
||||
if (RSA_sign(NID_sha1, (const uchar*)(sha1Buffer), shaSize, (uchar*)(signature.data()), &siglen, prKey) != 1) { // count signature
|
||||
LOG(("Error: Counting beta version signature failed!"));
|
||||
RSA_free(prKey);
|
||||
return QString();
|
||||
}
|
||||
RSA_free(prKey);
|
||||
|
||||
if (siglen != keySize) {
|
||||
LOG(("Error: Bad beta version signature length: %1").arg(siglen));
|
||||
return QString();
|
||||
}
|
||||
|
||||
signature = signature.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
|
||||
signature = signature.replace('-', '8').replace('_', 'B');
|
||||
return QString::fromUtf8(signature.mid(19, 32));
|
||||
}
|
||||
@@ -1,63 +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
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <QtNetwork/QLocalServer>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
class UpdateChecker : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
UpdateChecker(QThread *thread, const QString &url);
|
||||
|
||||
void unpackUpdate();
|
||||
|
||||
int32 ready();
|
||||
int32 size();
|
||||
|
||||
static void clearAll();
|
||||
|
||||
~UpdateChecker();
|
||||
|
||||
public slots:
|
||||
|
||||
void start();
|
||||
void partMetaGot();
|
||||
void partFinished(qint64 got, qint64 total);
|
||||
void partFailed(QNetworkReply::NetworkError e);
|
||||
void sendRequest();
|
||||
|
||||
private:
|
||||
void initOutput();
|
||||
|
||||
void fatalFail();
|
||||
|
||||
QString updateUrl;
|
||||
QNetworkAccessManager manager;
|
||||
QNetworkReply *reply;
|
||||
int32 already, full;
|
||||
QFile outputFile;
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
};
|
||||
|
||||
bool checkReadyUpdate();
|
||||
|
||||
#else // TDESKTOP_DISABLE_AUTOUPDATE
|
||||
class UpdateChecker : public QObject {
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
#endif // TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
QString countBetaVersionSignature(uint64 version);
|
||||
@@ -47,6 +47,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#define ARCH_CPU_X86_FAMILY 1
|
||||
#define ARCH_CPU_X86 1
|
||||
#define ARCH_CPU_32_BITS 1
|
||||
#elif defined(__aarch64__)
|
||||
#define ARCH_CPU_64_BITS 1
|
||||
#elif defined(_M_ARM) || defined(__arm__)
|
||||
#define ARCH_CPU_32_BITS 1
|
||||
#else
|
||||
#error Please add support for your architecture in base/build_config.h
|
||||
#endif
|
||||
|
||||
137
Telegram/SourceFiles/base/bytes.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
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 <gsl/gsl_byte>
|
||||
|
||||
namespace bytes {
|
||||
|
||||
using span = gsl::span<gsl::byte>;
|
||||
using const_span = gsl::span<const gsl::byte>;
|
||||
using vector = std::vector<gsl::byte>;
|
||||
|
||||
template <
|
||||
typename Container,
|
||||
typename = std::enable_if_t<!std::is_const_v<Container>>>
|
||||
inline span make_span(Container &container) {
|
||||
return gsl::as_writeable_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline const_span make_span(const Container &container) {
|
||||
return gsl::as_bytes(gsl::make_span(container));
|
||||
}
|
||||
|
||||
template <typename Type, std::ptrdiff_t Extent>
|
||||
inline span make_span(gsl::span<Type, Extent> container) {
|
||||
return gsl::as_writeable_bytes(container);
|
||||
}
|
||||
|
||||
template <typename Type, std::ptrdiff_t Extent>
|
||||
inline const_span make_span(gsl::span<const Type, Extent> container) {
|
||||
return gsl::as_bytes(container);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline span make_span(Type *value, std::size_t count) {
|
||||
return gsl::as_writeable_bytes(gsl::make_span(value, count));
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
inline const_span make_span(const Type *value, std::size_t count) {
|
||||
return gsl::as_bytes(gsl::make_span(value, count));
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
inline vector make_vector(const Container &container) {
|
||||
const auto buffer = bytes::make_span(container);
|
||||
return { buffer.begin(), buffer.end() };
|
||||
}
|
||||
|
||||
inline void copy(span destination, const_span source) {
|
||||
Expects(destination.size() >= source.size());
|
||||
|
||||
memcpy(destination.data(), source.data(), source.size());
|
||||
}
|
||||
|
||||
inline void move(span destination, const_span source) {
|
||||
Expects(destination.size() >= source.size());
|
||||
|
||||
memmove(destination.data(), source.data(), source.size());
|
||||
}
|
||||
|
||||
inline void set_with_const(span destination, gsl::byte value) {
|
||||
memset(
|
||||
destination.data(),
|
||||
gsl::to_integer<unsigned char>(value),
|
||||
destination.size());
|
||||
}
|
||||
|
||||
inline int compare(const_span a, const_span b) {
|
||||
const auto aSize = a.size(), bSize = b.size();
|
||||
return (aSize > bSize)
|
||||
? 1
|
||||
: (aSize < bSize)
|
||||
? -1
|
||||
: memcmp(a.data(), b.data(), aSize);
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template <typename Arg>
|
||||
std::size_t spansLength(Arg &&arg) {
|
||||
return bytes::make_span(arg).size();
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
std::size_t spansLength(Arg &&arg, Args &&...args) {
|
||||
return bytes::make_span(arg).size() + spansLength(args...);
|
||||
}
|
||||
|
||||
template <typename Arg>
|
||||
void spansAppend(span destination, Arg &&arg) {
|
||||
bytes::copy(destination, bytes::make_span(arg));
|
||||
}
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
void spansAppend(span destination, Arg &&arg, Args &&...args) {
|
||||
const auto data = bytes::make_span(arg);
|
||||
bytes::copy(destination, data);
|
||||
spansAppend(destination.subspan(data.size()), args...);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
|
||||
template <
|
||||
typename ...Args,
|
||||
typename = std::enable_if_t<(sizeof...(Args) > 1)>>
|
||||
vector concatenate(Args &&...args) {
|
||||
const auto size = details::spansLength(args...);
|
||||
auto result = vector(size);
|
||||
details::spansAppend(make_span(result), args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <
|
||||
typename SpanRange>
|
||||
vector concatenate(SpanRange args) {
|
||||
auto size = std::size_t(0);
|
||||
for (const auto &arg : args) {
|
||||
size += bytes::make_span(arg).size();
|
||||
}
|
||||
auto result = vector(size);
|
||||
auto buffer = make_span(result);
|
||||
for (const auto &arg : args) {
|
||||
const auto part = bytes::make_span(arg);
|
||||
bytes::copy(buffer, part);
|
||||
buffer = buffer.subspan(part.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace bytes
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "base/bytes.h"
|
||||
|
||||
namespace openssl {
|
||||
|
||||
@@ -223,3 +224,13 @@ inline int FillRandom(base::byte_span bytes) {
|
||||
}
|
||||
|
||||
} // namespace openssl
|
||||
|
||||
namespace bytes {
|
||||
|
||||
inline void set_random(span destination) {
|
||||
RAND_bytes(
|
||||
reinterpret_cast<unsigned char*>(destination.data()),
|
||||
destination.size());
|
||||
}
|
||||
|
||||
} // namespace bytes
|
||||
|
||||
@@ -38,4 +38,10 @@ QMap<QString, QString> url_parse_params(
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_ipv6(const QString &ip) {
|
||||
//static const auto regexp = QRegularExpression("^[a-fA-F0-9:]+$");
|
||||
//return regexp.match(ip).hasMatch();
|
||||
return ip.indexOf(':') >= 0;
|
||||
}
|
||||
|
||||
} // namespace qthelp
|
||||
|
||||
@@ -24,4 +24,6 @@ enum class UrlParamNameTransform {
|
||||
// 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);
|
||||
|
||||
bool is_ipv6(const QString &ip);
|
||||
|
||||
} // namespace qthelp
|
||||
|
||||
@@ -17,12 +17,26 @@ QObject *TimersAdjuster() {
|
||||
|
||||
} // namespace
|
||||
|
||||
Timer::Timer(base::lambda<void()> callback) : QObject(nullptr)
|
||||
Timer::Timer(
|
||||
not_null<QThread*> thread,
|
||||
base::lambda<void()> callback)
|
||||
: Timer(std::move(callback)) {
|
||||
moveToThread(thread);
|
||||
}
|
||||
|
||||
|
||||
Timer::Timer(base::lambda<void()> callback)
|
||||
: QObject(nullptr)
|
||||
, _callback(std::move(callback))
|
||||
, _type(Qt::PreciseTimer)
|
||||
, _adjusted(false) {
|
||||
setRepeat(Repeat::Interval);
|
||||
connect(TimersAdjuster(), &QObject::destroyed, this, [this] { adjust(); }, Qt::QueuedConnection);
|
||||
connect(
|
||||
TimersAdjuster(),
|
||||
&QObject::destroyed,
|
||||
this,
|
||||
[this] { adjust(); },
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void Timer::start(TimeMs timeout, Qt::TimerType type, Repeat repeat) {
|
||||
@@ -56,7 +70,11 @@ TimeMs Timer::remainingTime() const {
|
||||
|
||||
void Timer::Adjust() {
|
||||
QObject emitter;
|
||||
connect(&emitter, &QObject::destroyed, TimersAdjuster(), &QObject::destroyed);
|
||||
connect(
|
||||
&emitter,
|
||||
&QObject::destroyed,
|
||||
TimersAdjuster(),
|
||||
&QObject::destroyed);
|
||||
}
|
||||
|
||||
void Timer::adjust() {
|
||||
@@ -70,6 +88,7 @@ void Timer::adjust() {
|
||||
|
||||
void Timer::setTimeout(TimeMs timeout) {
|
||||
Expects(timeout >= 0 && timeout <= std::numeric_limits<int>::max());
|
||||
|
||||
_timeout = static_cast<unsigned int>(timeout);
|
||||
}
|
||||
|
||||
@@ -93,8 +112,12 @@ void Timer::timerEvent(QTimerEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
int DelayedCallTimer::call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type) {
|
||||
int DelayedCallTimer::call(
|
||||
TimeMs timeout,
|
||||
lambda_once<void()> callback,
|
||||
Qt::TimerType type) {
|
||||
Expects(timeout >= 0);
|
||||
|
||||
if (!callback) {
|
||||
return 0;
|
||||
}
|
||||
@@ -108,7 +131,7 @@ int DelayedCallTimer::call(TimeMs timeout, lambda_once<void()> callback, Qt::Tim
|
||||
void DelayedCallTimer::cancel(int callId) {
|
||||
if (callId) {
|
||||
killTimer(callId);
|
||||
_callbacks.erase(callId);
|
||||
_callbacks.remove(callId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@ namespace base {
|
||||
|
||||
class Timer final : private QObject {
|
||||
public:
|
||||
Timer(base::lambda<void()> callback = base::lambda<void()>());
|
||||
explicit Timer(
|
||||
not_null<QThread*> thread,
|
||||
base::lambda<void()> callback = nullptr);
|
||||
explicit Timer(base::lambda<void()> callback = nullptr);
|
||||
|
||||
static Qt::TimerType DefaultType(TimeMs timeout) {
|
||||
constexpr auto kThreshold = TimeMs(1000);
|
||||
@@ -85,17 +88,23 @@ private:
|
||||
class DelayedCallTimer final : private QObject {
|
||||
public:
|
||||
int call(TimeMs timeout, lambda_once<void()> callback) {
|
||||
return call(timeout, std::move(callback), Timer::DefaultType(timeout));
|
||||
return call(
|
||||
timeout,
|
||||
std::move(callback),
|
||||
Timer::DefaultType(timeout));
|
||||
}
|
||||
|
||||
int call(TimeMs timeout, lambda_once<void()> callback, Qt::TimerType type);
|
||||
int call(
|
||||
TimeMs timeout,
|
||||
lambda_once<void()> callback,
|
||||
Qt::TimerType type);
|
||||
void cancel(int callId);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *e) override;
|
||||
|
||||
private:
|
||||
std::map<int, lambda_once<void()>> _callbacks; // Better to use flatmap.
|
||||
base::flat_map<int, lambda_once<void()>> _callbacks;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "autoupdater.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "application.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@@ -18,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_boxes.h"
|
||||
#include "platform/platform_file_utilities.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/update_checker.h"
|
||||
|
||||
AboutBox::AboutBox(QWidget *parent)
|
||||
: _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink)
|
||||
@@ -68,7 +68,7 @@ void AboutBox::showVersionHistory() {
|
||||
case dbipLinux32: url += qsl("linux32/%1.tar.xz"); break;
|
||||
case dbipLinux64: url += qsl("linux/%1.tar.xz"); break;
|
||||
}
|
||||
url = url.arg(qsl("tbeta%1_%2").arg(cRealBetaVersion()).arg(countBetaVersionSignature(cRealBetaVersion())));
|
||||
url = url.arg(qsl("tbeta%1_%2").arg(cRealBetaVersion()).arg(Core::countBetaVersionSignature(cRealBetaVersion())));
|
||||
|
||||
Application::clipboard()->setText(url);
|
||||
|
||||
|
||||
@@ -19,12 +19,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
|
||||
QPointer<Ui::RoundButton> BoxContent::addButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback) {
|
||||
return addButton(std::move(textFactory), std::move(clickCallback), st::defaultBoxButton);
|
||||
QPointer<Ui::RoundButton> BoxContent::addButton(
|
||||
base::lambda<QString()> textFactory,
|
||||
base::lambda<void()> clickCallback) {
|
||||
return addButton(
|
||||
std::move(textFactory),
|
||||
std::move(clickCallback),
|
||||
st::defaultBoxButton);
|
||||
}
|
||||
|
||||
QPointer<Ui::RoundButton> BoxContent::addLeftButton(base::lambda<QString()> textFactory, base::lambda<void()> clickCallback) {
|
||||
return getDelegate()->addLeftButton(std::move(textFactory), std::move(clickCallback), st::defaultBoxButton);
|
||||
QPointer<Ui::RoundButton> BoxContent::addLeftButton(
|
||||
base::lambda<QString()> textFactory,
|
||||
base::lambda<void()> clickCallback) {
|
||||
return getDelegate()->addLeftButton(
|
||||
std::move(textFactory),
|
||||
std::move(clickCallback),
|
||||
st::defaultBoxButton);
|
||||
}
|
||||
|
||||
void BoxContent::setInner(object_ptr<TWidget> inner) {
|
||||
@@ -117,7 +127,7 @@ void BoxContent::updateShadowsVisibility() {
|
||||
(top > 0 || _innerTopSkip > 0),
|
||||
anim::type::normal);
|
||||
_bottomShadow->toggle(
|
||||
(top < _scroll->scrollTopMax()),
|
||||
(top < _scroll->scrollTopMax() || _innerBottomSkip > 0),
|
||||
anim::type::normal);
|
||||
}
|
||||
|
||||
@@ -145,6 +155,16 @@ void BoxContent::setInnerTopSkip(int innerTopSkip, bool scrollBottomFixed) {
|
||||
}
|
||||
}
|
||||
|
||||
void BoxContent::setInnerBottomSkip(int innerBottomSkip) {
|
||||
if (_innerBottomSkip != innerBottomSkip) {
|
||||
auto delta = innerBottomSkip - _innerBottomSkip;
|
||||
_innerBottomSkip = innerBottomSkip;
|
||||
if (_scroll && width() > 0) {
|
||||
updateScrollAreaGeometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoxContent::setInnerVisible(bool scrollAreaVisible) {
|
||||
if (_scroll) {
|
||||
_scroll->setVisible(scrollAreaVisible);
|
||||
@@ -169,13 +189,15 @@ void BoxContent::resizeEvent(QResizeEvent *e) {
|
||||
}
|
||||
|
||||
void BoxContent::updateScrollAreaGeometry() {
|
||||
auto newScrollHeight = height() - _innerTopSkip;
|
||||
auto newScrollHeight = height() - _innerTopSkip - _innerBottomSkip;
|
||||
auto changed = (_scroll->height() != newScrollHeight);
|
||||
_scroll->setGeometryToLeft(0, _innerTopSkip, width(), newScrollHeight);
|
||||
_topShadow->entity()->resize(width(), st::lineWidth);
|
||||
_topShadow->moveToLeft(0, _innerTopSkip);
|
||||
_bottomShadow->entity()->resize(width(), st::lineWidth);
|
||||
_bottomShadow->moveToLeft(0, height() - st::lineWidth);
|
||||
_bottomShadow->moveToLeft(
|
||||
0,
|
||||
height() - _innerBottomSkip - st::lineWidth);
|
||||
if (changed) {
|
||||
updateInnerVisibleTopBottom();
|
||||
|
||||
@@ -184,7 +206,7 @@ void BoxContent::updateScrollAreaGeometry() {
|
||||
(top > 0 || _innerTopSkip > 0),
|
||||
anim::type::instant);
|
||||
_bottomShadow->toggle(
|
||||
(top < _scroll->scrollTopMax()),
|
||||
(top < _scroll->scrollTopMax() || _innerBottomSkip > 0),
|
||||
anim::type::instant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,19 +130,29 @@ protected:
|
||||
getDelegate()->setDimensions(newWidth, maxHeight);
|
||||
}
|
||||
void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false);
|
||||
void setInnerBottomSkip(int bottomSkip);
|
||||
|
||||
template <typename Widget>
|
||||
QPointer<Widget> setInnerWidget(object_ptr<Widget> inner, const style::ScrollArea &st, int topSkip = 0) {
|
||||
QPointer<Widget> setInnerWidget(
|
||||
object_ptr<Widget> inner,
|
||||
const style::ScrollArea &st,
|
||||
int topSkip = 0,
|
||||
int bottomSkip = 0) {
|
||||
auto result = QPointer<Widget>(inner.data());
|
||||
setInnerTopSkip(topSkip);
|
||||
setInnerBottomSkip(bottomSkip);
|
||||
setInner(std::move(inner), st);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Widget>
|
||||
QPointer<Widget> setInnerWidget(object_ptr<Widget> inner, int topSkip = 0) {
|
||||
QPointer<Widget> setInnerWidget(
|
||||
object_ptr<Widget> inner,
|
||||
int topSkip = 0,
|
||||
int bottomSkip = 0) {
|
||||
auto result = QPointer<Widget>(inner.data());
|
||||
setInnerTopSkip(topSkip);
|
||||
setInnerBottomSkip(bottomSkip);
|
||||
setInner(std::move(inner));
|
||||
return result;
|
||||
}
|
||||
@@ -183,6 +193,7 @@ private:
|
||||
bool _preparing = false;
|
||||
bool _noContentMargin = false;
|
||||
int _innerTopSkip = 0;
|
||||
int _innerBottomSkip = 0;
|
||||
object_ptr<Ui::ScrollArea> _scroll = { nullptr };
|
||||
object_ptr<Ui::FadeShadow> _topShadow = { nullptr };
|
||||
object_ptr<Ui::FadeShadow> _bottomShadow = { nullptr };
|
||||
|
||||
@@ -33,7 +33,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxGroupChannelTitle = 255;
|
||||
constexpr auto kMaxChannelDescription = 255;
|
||||
constexpr auto kMaxChannelDescription = 255; // See also edit_peer_info_box.
|
||||
constexpr auto kMaxBioLength = 70;
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
|
||||
|
||||
@@ -117,6 +117,11 @@ boxLabel: FlatLabel(defaultFlatLabel) {
|
||||
align: align(topleft);
|
||||
style: boxLabelStyle;
|
||||
}
|
||||
boxDividerLabel: FlatLabel(boxLabel) {
|
||||
minWidth: 245px;
|
||||
textFg: windowSubTextFg;
|
||||
style: defaultTextStyle;
|
||||
}
|
||||
|
||||
countryRowHeight: 36px;
|
||||
countryRowNameFont: semiboldFont;
|
||||
@@ -711,3 +716,68 @@ sendMediaFileThumbSize: 64px;
|
||||
sendMediaFileThumbSkip: 10px;
|
||||
sendMediaFileNameTop: 7px;
|
||||
sendMediaFileStatusTop: 37px;
|
||||
|
||||
proxyUsePadding: margins(22px, 6px, 22px, 5px);
|
||||
proxyTryIPv6Padding: margins(22px, 8px, 22px, 5px);
|
||||
proxyRowPadding: margins(22px, 8px, 8px, 8px);
|
||||
proxyRowIconSkip: 32px;
|
||||
proxyRowSkip: 2px;
|
||||
proxyRowRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
proxyRowSelectedIcon: icon {{ "passport_ready", windowActiveTextFg }};
|
||||
proxyRowTitleFg: windowFg;
|
||||
proxyRowTitlePalette: TextPalette(defaultTextPalette) {
|
||||
linkFg: windowSubTextFg;
|
||||
}
|
||||
proxyRowTitleStyle: TextStyle(defaultTextStyle) {
|
||||
font: semiboldFont;
|
||||
linkFont: normalFont;
|
||||
linkFontOver: normalFont;
|
||||
}
|
||||
proxyRowStatusFg: windowSubTextFg;
|
||||
proxyRowStatusFgOnline: windowActiveTextFg;
|
||||
proxyRowStatusFgOffline: boxTextFgError;
|
||||
proxyRowStatusFgAvailable: boxTextFgGood;
|
||||
proxyRowEdit: IconButton(defaultIconButton) {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
icon: icon {{ "settings_edit_name", menuIconFg }};
|
||||
iconOver: icon {{ "settings_edit_name", menuIconFgOver }};
|
||||
iconPosition: point(12px, 13px);
|
||||
|
||||
rippleAreaSize: 40px;
|
||||
rippleAreaPosition: point(0px, 0px);
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: windowBgOver;
|
||||
}
|
||||
}
|
||||
|
||||
proxyEditTitle: FlatLabel(defaultFlatLabel) {
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
font: autoDownloadTitleFont;
|
||||
}
|
||||
textFg: boxTitleFg;
|
||||
}
|
||||
proxyEditTitlePadding: margins(22px, 16px, 22px, 0px);
|
||||
proxyEditTypePadding: margins(22px, 4px, 22px, 8px);
|
||||
proxyEditInputPadding: margins(22px, 0px, 22px, 0px);
|
||||
proxyEditSkip: 16px;
|
||||
|
||||
proxyEmptyListLabel: FlatLabel(defaultFlatLabel) {
|
||||
align: align(top);
|
||||
textFg: windowSubTextFg;
|
||||
}
|
||||
proxyEmptyListPadding: margins(22px, 48px, 22px, 0px);
|
||||
proxyCheckingPosition: point(2px, 5px);
|
||||
proxyCheckingSkip: 6px;
|
||||
proxyCheckingAnimation: InfiniteRadialAnimation(defaultInfiniteRadialAnimation) {
|
||||
color: windowSubTextFg;
|
||||
thickness: 1px;
|
||||
size: size(8px, 8px);
|
||||
}
|
||||
proxyDropdownDownPosition: point(-2px, 35px);
|
||||
proxyDropdownUpPosition: point(-2px, 20px);
|
||||
|
||||
proxyAboutPadding: margins(22px, 7px, 22px, 14px);
|
||||
|
||||
@@ -641,7 +641,7 @@ ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, bool isChanne
|
||||
}
|
||||
|
||||
void ConfirmInviteBox::prepare() {
|
||||
addButton(langFactory(lng_group_invite_join), [this] {
|
||||
addButton(langFactory(lng_group_invite_join), [] {
|
||||
if (auto main = App::main()) {
|
||||
main->onInviteImport();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#pragma once
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "base/timer.h"
|
||||
#include "mtproto/connection_abstract.h"
|
||||
|
||||
namespace Ui {
|
||||
class InputField;
|
||||
@@ -24,9 +26,13 @@ class ConnectionBox : public BoxContent {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using Type = ProxyData::Type;
|
||||
|
||||
ConnectionBox(QWidget *parent);
|
||||
|
||||
static void ShowApplyProxyConfirmation(const QMap<QString, QString> &fields);
|
||||
static void ShowApplyProxyConfirmation(
|
||||
Type type,
|
||||
const QMap<QString, QString> &fields);
|
||||
|
||||
protected:
|
||||
void prepare() override;
|
||||
@@ -40,19 +46,20 @@ private slots:
|
||||
void onSave();
|
||||
|
||||
private:
|
||||
void typeChanged(DBIConnectionType type);
|
||||
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<DBIConnectionType>> _typeGroup;
|
||||
object_ptr<Ui::Radioenum<DBIConnectionType>> _autoRadio;
|
||||
object_ptr<Ui::Radioenum<DBIConnectionType>> _httpProxyRadio;
|
||||
object_ptr<Ui::Radioenum<DBIConnectionType>> _tcpProxyRadio;
|
||||
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;
|
||||
|
||||
};
|
||||
@@ -84,3 +91,90 @@ private:
|
||||
int _sectionHeight = 0;
|
||||
|
||||
};
|
||||
|
||||
class ProxiesBoxController : public base::Subscriber {
|
||||
public:
|
||||
using Type = ProxyData::Type;
|
||||
|
||||
ProxiesBoxController();
|
||||
|
||||
static object_ptr<BoxContent> CreateOwningBox();
|
||||
object_ptr<BoxContent> create();
|
||||
|
||||
enum class ItemState {
|
||||
Connecting,
|
||||
Online,
|
||||
Checking,
|
||||
Available,
|
||||
Unavailable
|
||||
};
|
||||
struct ItemView {
|
||||
int id = 0;
|
||||
QString type;
|
||||
QString host;
|
||||
uint32 port = 0;
|
||||
int ping = 0;
|
||||
bool selected = false;
|
||||
bool deleted = false;
|
||||
bool supportsShare = false;
|
||||
bool supportsCalls = false;
|
||||
ItemState state = ItemState::Checking;
|
||||
|
||||
};
|
||||
|
||||
void deleteItem(int id);
|
||||
void restoreItem(int id);
|
||||
void shareItem(int id);
|
||||
void applyItem(int id);
|
||||
object_ptr<BoxContent> editItemBox(int id);
|
||||
object_ptr<BoxContent> addNewItemBox();
|
||||
bool setProxyEnabled(bool enabled);
|
||||
void setProxyForCalls(bool enabled);
|
||||
void setTryIPv6(bool enabled);
|
||||
rpl::producer<bool> proxyEnabledValue() const;
|
||||
|
||||
rpl::producer<ItemView> views() const;
|
||||
|
||||
~ProxiesBoxController();
|
||||
|
||||
private:
|
||||
using Checker = MTP::internal::ConnectionPointer;
|
||||
struct Item {
|
||||
int id = 0;
|
||||
ProxyData data;
|
||||
bool deleted = false;
|
||||
Checker checker;
|
||||
Checker checkerv6;
|
||||
ItemState state = ItemState::Checking;
|
||||
int ping = 0;
|
||||
|
||||
};
|
||||
|
||||
std::vector<Item>::iterator findById(int id);
|
||||
std::vector<Item>::iterator findByProxy(const ProxyData &proxy);
|
||||
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);
|
||||
|
||||
void replaceItemWith(
|
||||
std::vector<Item>::iterator which,
|
||||
std::vector<Item>::iterator with);
|
||||
void replaceItemValue(
|
||||
std::vector<Item>::iterator which,
|
||||
const ProxyData &proxy);
|
||||
void addNewItem(const ProxyData &proxy);
|
||||
|
||||
int _idCounter = 0;
|
||||
std::vector<Item> _list;
|
||||
rpl::event_stream<ItemView> _views;
|
||||
base::Timer _saveTimer;
|
||||
rpl::event_stream<bool> _proxyEnabledChanges;
|
||||
|
||||
ProxyData _lastSelectedProxy;
|
||||
bool _lastSelectedProxyUsed = false;
|
||||
|
||||
};
|
||||
|
||||
@@ -469,7 +469,7 @@ void EditRestrictedBox::createUntilVariants() {
|
||||
_untilVariants.back()->setDisabled(true);
|
||||
}
|
||||
};
|
||||
auto addCustomVariant = [this, addVariant](TimeId until, TimeId from, TimeId to) {
|
||||
auto addCustomVariant = [addVariant](TimeId until, TimeId from, TimeId to) {
|
||||
if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) {
|
||||
addVariant(
|
||||
until,
|
||||
|
||||
@@ -95,7 +95,7 @@ void EditPrivacyBox::prepare() {
|
||||
|
||||
int EditPrivacyBox::resizeGetHeight(int newWidth) {
|
||||
auto top = 0;
|
||||
auto layoutRow = [this, newWidth, &top](auto &widget, style::margins padding) {
|
||||
auto layoutRow = [&](auto &widget, style::margins padding) {
|
||||
if (!widget) return;
|
||||
widget->resizeToNaturalWidth(newWidth - padding.left() - padding.right());
|
||||
widget->moveToLeft(padding.left(), top + padding.top());
|
||||
@@ -137,7 +137,7 @@ int EditPrivacyBox::countDefaultHeight(int newWidth) {
|
||||
}
|
||||
return st::editPrivacyOptionMargin.top() + st::defaultCheck.diameter + st::editPrivacyOptionMargin.bottom();
|
||||
};
|
||||
auto labelHeight = [this, newWidth](const QString &text, const style::FlatLabel &st, style::margins padding) {
|
||||
auto labelHeight = [newWidth](const QString &text, const style::FlatLabel &st, style::margins padding) {
|
||||
if (text.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -731,7 +731,7 @@ void EditChatAdminsBoxController::Start(not_null<ChatData*> chat) {
|
||||
}
|
||||
|
||||
void AddBotToGroupBoxController::Start(not_null<UserData*> bot) {
|
||||
auto initBox = [bot](not_null<PeerListBox*> box) {
|
||||
auto initBox = [=](not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddBotToGroupBoxController>(bot), std::move(initBox)));
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace {
|
||||
|
||||
constexpr auto kUsernameCheckTimeout = TimeMs(200);
|
||||
constexpr auto kMinUsernameLength = 5;
|
||||
constexpr auto kMaxChannelDescription = 255; // See also add_contact_box.
|
||||
|
||||
class Controller
|
||||
: private MTP::Sender
|
||||
@@ -324,6 +325,7 @@ object_ptr<Ui::RpWidget> Controller::createDescriptionEdit() {
|
||||
langFactory(lng_create_group_description),
|
||||
channel->about()),
|
||||
st::editPeerDescriptionMargins);
|
||||
result->entity()->setMaxLength(kMaxChannelDescription);
|
||||
|
||||
QObject::connect(
|
||||
result->entity(),
|
||||
@@ -1252,11 +1254,9 @@ void Controller::saveTitle() {
|
||||
continueSave();
|
||||
return;
|
||||
}
|
||||
_controls.title->showError();
|
||||
if (type == qstr("NO_CHAT_TITLE")) {
|
||||
_controls.title->showError();
|
||||
_box->scrollToWidget(_controls.title);
|
||||
} else {
|
||||
_controls.title->setFocus();
|
||||
}
|
||||
cancelSave();
|
||||
};
|
||||
@@ -1302,7 +1302,7 @@ void Controller::saveDescription() {
|
||||
successCallback();
|
||||
return;
|
||||
}
|
||||
_controls.description->setFocus();
|
||||
_controls.description->showError();
|
||||
cancelSave();
|
||||
}).send();
|
||||
}
|
||||
|
||||
@@ -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(); });
|
||||
|
||||
_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();
|
||||
}
|
||||
@@ -90,7 +121,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 +129,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) {
|
||||
|
||||
@@ -21,7 +21,8 @@ class ReportBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ReportBox(QWidget*, PeerData *peer);
|
||||
ReportBox(QWidget*, not_null<PeerData*> peer);
|
||||
ReportBox(QWidget*, not_null<PeerData*> peer, MessageIdsList ids);
|
||||
|
||||
private slots:
|
||||
void onReport();
|
||||
@@ -49,13 +50,14 @@ private:
|
||||
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::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::InputArea> _reasonOtherText = { nullptr };
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
@@ -1581,18 +1581,21 @@ void SendFilesBox::captionResized() {
|
||||
update();
|
||||
}
|
||||
|
||||
bool SendFilesBox::canAddUrls(const QList<QUrl> &urls) const {
|
||||
return !urls.isEmpty() && ranges::find_if(
|
||||
urls,
|
||||
[](const QUrl &url) { return !url.isLocalFile(); }
|
||||
) == urls.end();
|
||||
}
|
||||
|
||||
bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
|
||||
auto files = 0;
|
||||
if (data->hasUrls()) {
|
||||
for (const auto &url : data->urls()) {
|
||||
if (url.isLocalFile()) {
|
||||
++files;
|
||||
}
|
||||
}
|
||||
} else if (data->hasImage()) {
|
||||
++files;
|
||||
const auto urls = data->hasUrls() ? data->urls() : QList<QUrl>();
|
||||
auto filesCount = canAddUrls(urls) ? urls.size() : 0;
|
||||
if (!filesCount && data->hasImage()) {
|
||||
++filesCount;
|
||||
}
|
||||
if (_list.files.size() + files > Storage::MaxAlbumItems()) {
|
||||
|
||||
if (_list.files.size() + filesCount > Storage::MaxAlbumItems()) {
|
||||
return false;
|
||||
} else if (_list.files.size() > 1 && !_albumPreview) {
|
||||
return false;
|
||||
@@ -1605,10 +1608,14 @@ bool SendFilesBox::canAddFiles(not_null<const QMimeData*> data) const {
|
||||
|
||||
bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||
auto list = [&] {
|
||||
if (data->hasUrls()) {
|
||||
return Storage::PrepareMediaList(
|
||||
data->urls(),
|
||||
st::sendMediaPreviewSize);
|
||||
const auto urls = data->hasUrls() ? data->urls() : QList<QUrl>();
|
||||
auto result = canAddUrls(urls)
|
||||
? Storage::PrepareMediaList(urls, st::sendMediaPreviewSize)
|
||||
: Storage::PreparedList(
|
||||
Storage::PreparedList::Error::EmptyFile,
|
||||
QString());
|
||||
if (result.error == Storage::PreparedList::Error::None) {
|
||||
return result;
|
||||
} else if (data->hasImage()) {
|
||||
auto image = qvariant_cast<QImage>(data->imageData());
|
||||
if (!image.isNull()) {
|
||||
@@ -1618,9 +1625,7 @@ bool SendFilesBox::addFiles(not_null<const QMimeData*> data) {
|
||||
st::sendMediaPreviewSize);
|
||||
}
|
||||
}
|
||||
return Storage::PreparedList(
|
||||
Storage::PreparedList::Error::EmptyFile,
|
||||
QString());
|
||||
return result;
|
||||
}();
|
||||
if (_list.files.size() + list.files.size() > Storage::MaxAlbumItems()) {
|
||||
return false;
|
||||
|
||||
@@ -84,6 +84,7 @@ private:
|
||||
void updateControlsGeometry();
|
||||
|
||||
bool canAddFiles(not_null<const QMimeData*> data) const;
|
||||
bool canAddUrls(const QList<QUrl> &urls) const;
|
||||
bool addFiles(not_null<const QMimeData*> data);
|
||||
|
||||
QString _titleText;
|
||||
|
||||
@@ -573,15 +573,23 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
++indexFrom;
|
||||
}
|
||||
} else {
|
||||
// empty
|
||||
p.setFont(st::noContactsFont);
|
||||
p.setPen(st::noContactsColor);
|
||||
p.drawText(
|
||||
rect().marginsRemoved(st::boxPadding),
|
||||
lang(lng_bot_no_chats),
|
||||
style::al_center);
|
||||
}
|
||||
} else {
|
||||
if (_filtered.isEmpty() && _byUsernameFiltered.empty()) {
|
||||
// empty
|
||||
if (_filtered.isEmpty()
|
||||
&& _byUsernameFiltered.empty()
|
||||
&& !_searching) {
|
||||
p.setFont(st::noContactsFont);
|
||||
p.setPen(st::noContactsColor);
|
||||
p.drawText(
|
||||
rect().marginsRemoved(st::boxPadding),
|
||||
lang(lng_bot_chats_not_found),
|
||||
style::al_center);
|
||||
} else {
|
||||
auto filteredSize = _filtered.size();
|
||||
if (filteredSize) {
|
||||
@@ -781,7 +789,9 @@ void ShareBox::Inner::peopleReceived(
|
||||
const QVector<MTPPeer> &my,
|
||||
const QVector<MTPPeer> &people) {
|
||||
_lastQuery = query.toLower().trimmed();
|
||||
if (_lastQuery.at(0) == '@') _lastQuery = _lastQuery.mid(1);
|
||||
if (_lastQuery.at(0) == '@') {
|
||||
_lastQuery = _lastQuery.mid(1);
|
||||
}
|
||||
int32 already = _byUsernameFiltered.size();
|
||||
_byUsernameFiltered.reserve(already + my.size() + people.size());
|
||||
d_byUsernameFiltered.reserve(already + my.size() + people.size());
|
||||
|
||||
@@ -40,17 +40,30 @@ constexpr auto kHangupTimeoutMs = 5000;
|
||||
|
||||
using tgvoip::Endpoint;
|
||||
|
||||
void ConvertEndpoint(std::vector<tgvoip::Endpoint> &ep, const MTPDphoneConnection &mtc) {
|
||||
void ConvertEndpoint(
|
||||
std::vector<tgvoip::Endpoint> &ep,
|
||||
const MTPDphoneConnection &mtc) {
|
||||
if (mtc.vpeer_tag.v.length() != 16) {
|
||||
return;
|
||||
}
|
||||
auto ipv4 = tgvoip::IPv4Address(std::string(mtc.vip.v.constData(), mtc.vip.v.size()));
|
||||
auto ipv6 = tgvoip::IPv6Address(std::string(mtc.vipv6.v.constData(), mtc.vipv6.v.size()));
|
||||
ep.push_back(Endpoint((int64_t)mtc.vid.v, (uint16_t)mtc.vport.v, ipv4, ipv6, EP_TYPE_UDP_RELAY, (unsigned char*)mtc.vpeer_tag.v.data()));
|
||||
auto ipv4 = tgvoip::IPv4Address(std::string(
|
||||
mtc.vip.v.constData(),
|
||||
mtc.vip.v.size()));
|
||||
auto ipv6 = tgvoip::IPv6Address(std::string(
|
||||
mtc.vipv6.v.constData(),
|
||||
mtc.vipv6.v.size()));
|
||||
ep.push_back(Endpoint(
|
||||
(int64_t)mtc.vid.v,
|
||||
(uint16_t)mtc.vport.v,
|
||||
ipv4,
|
||||
ipv6,
|
||||
EP_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(
|
||||
const std::array<gsl::byte, kFingerprintDataSize> &authKey) {
|
||||
auto hash = openssl::Sha1(authKey);
|
||||
return (gsl::to_integer<uint64>(hash[19]) << 56)
|
||||
| (gsl::to_integer<uint64>(hash[18]) << 48)
|
||||
@@ -64,7 +77,10 @@ uint64 ComputeFingerprint(const std::array<gsl::byte, kFingerprintDataSize> &aut
|
||||
|
||||
} // namespace
|
||||
|
||||
Call::Call(not_null<Delegate*> delegate, not_null<UserData*> user, Type type)
|
||||
Call::Call(
|
||||
not_null<Delegate*> delegate,
|
||||
not_null<UserData*> user,
|
||||
Type type)
|
||||
: _delegate(delegate)
|
||||
, _user(user)
|
||||
, _type(type) {
|
||||
@@ -124,7 +140,16 @@ void Call::startOutgoing() {
|
||||
Expects(_type == Type::Outgoing);
|
||||
Expects(_state == State::Requesting);
|
||||
|
||||
request(MTPphone_RequestCall(_user->inputUser, MTP_int(rand_value<int32>()), MTP_bytes(_gaHash), MTP_phoneCallProtocol(MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), MTP_int(kMaxLayer)))).done([this](const MTPphone_PhoneCall &result) {
|
||||
request(MTPphone_RequestCall(
|
||||
_user->inputUser,
|
||||
MTP_int(rand_value<int32>()),
|
||||
MTP_bytes(_gaHash),
|
||||
MTP_phoneCallProtocol(
|
||||
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
|
||||
| MTPDphoneCallProtocol::Flag::f_udp_reflector),
|
||||
MTP_int(kMinLayer),
|
||||
MTP_int(kMaxLayer))
|
||||
)).done([this](const MTPphone_PhoneCall &result) {
|
||||
Expects(result.type() == mtpc_phone_phoneCall);
|
||||
|
||||
setState(State::Waiting);
|
||||
@@ -185,7 +210,15 @@ void Call::answer() {
|
||||
} else {
|
||||
_answerAfterDhConfigReceived = false;
|
||||
}
|
||||
request(MTPphone_AcceptCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_bytes(_gb), _protocol)).done([this](const MTPphone_PhoneCall &result) {
|
||||
request(MTPphone_AcceptCall(
|
||||
MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)),
|
||||
MTP_bytes(_gb),
|
||||
MTP_phoneCallProtocol(
|
||||
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
|
||||
| MTPDphoneCallProtocol::Flag::f_udp_reflector),
|
||||
MTP_int(kMinLayer),
|
||||
MTP_int(kMaxLayer))
|
||||
)).done([this](const MTPphone_PhoneCall &result) {
|
||||
Expects(result.type() == mtpc_phone_phoneCall);
|
||||
auto &call = result.c_phone_phoneCall();
|
||||
App::feedUsers(call.vusers);
|
||||
@@ -294,7 +327,6 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
|
||||
}
|
||||
_id = data.vid.v;
|
||||
_accessHash = data.vaccess_hash.v;
|
||||
_protocol = data.vprotocol;
|
||||
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()));
|
||||
@@ -393,7 +425,16 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
|
||||
_keyFingerprint = ComputeFingerprint(_authKey);
|
||||
|
||||
setState(State::ExchangingKeys);
|
||||
request(MTPphone_ConfirmCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_bytes(_ga), MTP_long(_keyFingerprint), MTP_phoneCallProtocol(MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), MTP_int(kMaxLayer)))).done([this](const MTPphone_PhoneCall &result) {
|
||||
request(MTPphone_ConfirmCall(
|
||||
MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)),
|
||||
MTP_bytes(_ga),
|
||||
MTP_long(_keyFingerprint),
|
||||
MTP_phoneCallProtocol(
|
||||
MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p
|
||||
| MTPDphoneCallProtocol::Flag::f_udp_reflector),
|
||||
MTP_int(kMinLayer),
|
||||
MTP_int(kMaxLayer))
|
||||
)).done([this](const MTPphone_PhoneCall &result) {
|
||||
Expects(result.type() == mtpc_phone_phoneCall);
|
||||
auto &call = result.c_phone_phoneCall();
|
||||
App::feedUsers(call.vusers);
|
||||
@@ -480,6 +521,18 @@ void Call::createAndStartController(const MTPDphoneCall &call) {
|
||||
_controller->SetStateCallback([](tgvoip::VoIPController *controller, int state) {
|
||||
static_cast<Call*>(controller->implData)->handleControllerStateChange(controller, state);
|
||||
});
|
||||
if (Global::UseProxy() && Global::UseProxyForCalls()) {
|
||||
const auto proxy = Global::SelectedProxy();
|
||||
if (proxy.supportsCalls()) {
|
||||
Assert(proxy.type == ProxyData::Type::Socks5);
|
||||
_controller->SetProxy(
|
||||
tgvoip::PROXY_SOCKS5,
|
||||
proxy.host.toStdString(),
|
||||
proxy.port,
|
||||
proxy.user.toStdString(),
|
||||
proxy.password.toStdString());
|
||||
}
|
||||
}
|
||||
_controller->Start();
|
||||
_controller->Connect();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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);
|
||||
|
||||
@@ -14,11 +14,15 @@ namespace Ui {
|
||||
namespace Emoji {
|
||||
|
||||
inline utf16string QStringToUTF16(const QString &string) {
|
||||
return utf16string(reinterpret_cast<const utf16char*>(string.constData()), string.size());
|
||||
return utf16string(
|
||||
reinterpret_cast<const utf16char*>(string.constData()),
|
||||
string.size());
|
||||
}
|
||||
|
||||
inline QString QStringFromUTF16(utf16string string) {
|
||||
return QString::fromRawData(reinterpret_cast<const QChar*>(string.data()), string.size());
|
||||
return QString::fromRawData(
|
||||
reinterpret_cast<const QChar*>(string.data()),
|
||||
string.size());
|
||||
}
|
||||
|
||||
constexpr auto kSuggestionMaxLength = internal::kReplacementMaxLength;
|
||||
|
||||
@@ -346,12 +346,15 @@ void SuggestionsWidget::leaveEventHook(QEvent *e) {
|
||||
return TWidget::leaveEventHook(e);
|
||||
}
|
||||
|
||||
SuggestionsController::SuggestionsController(QWidget *parent, not_null<QTextEdit*> field) : QObject(nullptr)
|
||||
SuggestionsController::SuggestionsController(QWidget *parent, not_null<QTextEdit*> field)
|
||||
: QObject(nullptr)
|
||||
, _field(field)
|
||||
, _container(parent, st::emojiSuggestionsDropdown)
|
||||
, _suggestions(_container->setOwnedWidget(object_ptr<Ui::Emoji::SuggestionsWidget>(parent, st::emojiSuggestionsMenu))) {
|
||||
_container->setAutoHiding(false);
|
||||
|
||||
setReplaceCallback(nullptr);
|
||||
|
||||
_field->installEventFilter(this);
|
||||
connect(_field, &QTextEdit::textChanged, this, [this] { handleTextChange(); });
|
||||
connect(_field, &QTextEdit::cursorPositionChanged, this, [this] { handleCursorPositionChange(); });
|
||||
@@ -363,6 +366,23 @@ SuggestionsController::SuggestionsController(QWidget *parent, not_null<QTextEdit
|
||||
handleTextChange();
|
||||
}
|
||||
|
||||
void SuggestionsController::setReplaceCallback(
|
||||
base::lambda<void(
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement)> callback) {
|
||||
if (callback) {
|
||||
_replaceCallback = std::move(callback);
|
||||
} else {
|
||||
_replaceCallback = [=](int from, int till, const QString &replacement) {
|
||||
auto cursor = _field->textCursor();
|
||||
cursor.setPosition(from);
|
||||
cursor.setPosition(till, QTextCursor::KeepAnchor);
|
||||
cursor.insertText(replacement);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void SuggestionsController::handleTextChange() {
|
||||
_ignoreCursorPositionChange = true;
|
||||
InvokeQueued(this, [this] { _ignoreCursorPositionChange = false; });
|
||||
@@ -374,7 +394,7 @@ void SuggestionsController::handleTextChange() {
|
||||
}
|
||||
|
||||
QString SuggestionsController::getEmojiQuery() {
|
||||
if (!cReplaceEmojis()) {
|
||||
if (!Global::SuggestEmoji()) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -411,10 +431,23 @@ QString SuggestionsController::getEmojiQuery() {
|
||||
return QString();
|
||||
}
|
||||
|
||||
auto isSuggestionChar = [](QChar ch) {
|
||||
return (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_') || (ch == '-') || (ch == '+');
|
||||
const auto isUpperCaseLetter = [](QChar ch) {
|
||||
return (ch >= 'A' && ch <= 'Z');
|
||||
};
|
||||
auto isGoodCharBeforeSuggestion = [isSuggestionChar](QChar ch) {
|
||||
const auto isLetter = [](QChar ch) {
|
||||
return (ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9');
|
||||
};
|
||||
const auto isSuggestionChar = [](QChar ch) {
|
||||
return (ch >= 'a' && ch <= 'z')
|
||||
|| (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| (ch == '_')
|
||||
|| (ch == '-')
|
||||
|| (ch == '+');
|
||||
};
|
||||
const auto isGoodCharBeforeSuggestion = [&](QChar ch) {
|
||||
return !isSuggestionChar(ch) || (ch == 0);
|
||||
};
|
||||
Assert(position > 0 && position <= text.size());
|
||||
@@ -427,7 +460,22 @@ QString SuggestionsController::getEmojiQuery() {
|
||||
if (position > i + 1) {
|
||||
// Skip colon and the first letter.
|
||||
_queryStartPosition += i + 2;
|
||||
return text.mid(i, position - i);
|
||||
const auto length = position - i;
|
||||
auto result = text.mid(i, length);
|
||||
const auto upperCaseLetters = std::count_if(
|
||||
result.begin(),
|
||||
result.end(),
|
||||
isUpperCaseLetter);
|
||||
const auto letters = std::count_if(
|
||||
result.begin(),
|
||||
result.end(),
|
||||
isLetter);
|
||||
if (letters == upperCaseLetters && letters == 1) {
|
||||
// No upper case single letter suggestions.
|
||||
// We don't want to suggest emoji on :D and :-P
|
||||
return QString();
|
||||
}
|
||||
return result.toLower();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
@@ -443,24 +491,14 @@ QString SuggestionsController::getEmojiQuery() {
|
||||
}
|
||||
|
||||
void SuggestionsController::replaceCurrent(const QString &replacement) {
|
||||
auto cursor = _field->textCursor();
|
||||
auto suggestion = getEmojiQuery();
|
||||
if (suggestion.isEmpty()) {
|
||||
_suggestions->showWithQuery(QString());
|
||||
} else {
|
||||
cursor.setPosition(cursor.position() - suggestion.size(), QTextCursor::KeepAnchor);
|
||||
cursor.insertText(replacement);
|
||||
}
|
||||
|
||||
auto emojiText = GetSuggestionEmoji(QStringToUTF16(replacement));
|
||||
if (auto emoji = Find(QStringFromUTF16(emojiText))) {
|
||||
if (emoji->hasVariants()) {
|
||||
auto it = cEmojiVariants().constFind(emoji->nonColoredId());
|
||||
if (it != cEmojiVariants().cend()) {
|
||||
emoji = emoji->variant(it.value());
|
||||
}
|
||||
}
|
||||
AddRecent(emoji);
|
||||
const auto cursor = _field->textCursor();
|
||||
const auto position = cursor.position();
|
||||
const auto from = position - suggestion.size();
|
||||
_replaceCallback(from, position, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,10 @@ public:
|
||||
SuggestionsController(QWidget *parent, not_null<QTextEdit*> field);
|
||||
|
||||
void raise();
|
||||
void setReplaceCallback(base::lambda<void(
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement)> callback);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
@@ -88,6 +92,10 @@ private:
|
||||
bool _ignoreCursorPositionChange = false;
|
||||
bool _textChangeAfterKeyPress = false;
|
||||
QPointer<QTextEdit> _field;
|
||||
base::lambda<void(
|
||||
int from,
|
||||
int till,
|
||||
const QString &replacement)> _replaceCallback;
|
||||
object_ptr<InnerDropdown> _container;
|
||||
QPointer<SuggestionsWidget> _suggestions;
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "emoji_suggestions_data.h"
|
||||
#include "chat_helpers/emoji_suggestions_helper.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
|
||||
@@ -115,6 +117,26 @@ MessageField::MessageField(QWidget *parent, not_null<Window::Controller*> contro
|
||||
setMaxHeight(st::historyComposeFieldMaxHeight);
|
||||
|
||||
setTagMimeProcessor(std::make_unique<FieldTagMimeProcessor>());
|
||||
|
||||
addInstantReplace("--", QString(1, QChar(8212)));
|
||||
addInstantReplace("<<", QString(1, QChar(171)));
|
||||
addInstantReplace(">>", QString(1, QChar(187)));
|
||||
const auto &replacements = Ui::Emoji::internal::GetAllReplacements();
|
||||
for (const auto &one : replacements) {
|
||||
const auto with = Ui::Emoji::QStringFromUTF16(one.emoji);
|
||||
const auto what = Ui::Emoji::QStringFromUTF16(one.replacement);
|
||||
addInstantReplace(what, with);
|
||||
}
|
||||
const auto &pairs = Ui::Emoji::internal::GetReplacementPairs();
|
||||
for (const auto &[what, index] : pairs) {
|
||||
const auto emoji = Ui::Emoji::internal::ByIndex(index);
|
||||
Assert(emoji != nullptr);
|
||||
addInstantReplace(what, emoji->text());
|
||||
}
|
||||
enableInstantReplaces(Global::ReplaceEmoji());
|
||||
subscribe(Global::RefReplaceEmojiChanged(), [=] {
|
||||
enableInstantReplaces(Global::ReplaceEmoji());
|
||||
});
|
||||
}
|
||||
|
||||
bool MessageField::hasSendText() const {
|
||||
|
||||
@@ -783,9 +783,9 @@ std::vector<not_null<DocumentData*>> GetListByEmoji(
|
||||
addList(
|
||||
Auth().data().stickerSetsOrder(),
|
||||
MTPDstickerSet::Flag::f_archived);
|
||||
addList(
|
||||
Auth().data().featuredStickerSetsOrder(),
|
||||
MTPDstickerSet::Flag::f_installed_date);
|
||||
//addList(
|
||||
// Auth().data().featuredStickerSetsOrder(),
|
||||
// MTPDstickerSet::Flag::f_installed_date);
|
||||
|
||||
if (!setsToRequest.empty()) {
|
||||
for (const auto [setId, accessHash] : setsToRequest) {
|
||||
@@ -794,13 +794,15 @@ std::vector<not_null<DocumentData*>> GetListByEmoji(
|
||||
Auth().api().requestStickerSets();
|
||||
}
|
||||
|
||||
const auto others = Auth().api().stickersByEmoji(original);
|
||||
if (!others) {
|
||||
return {};
|
||||
}
|
||||
result.reserve(result.size() + others->size());
|
||||
for (const auto document : *others) {
|
||||
add(document, CreateOtherSortKey(document));
|
||||
if (Global::SuggestStickersByEmoji()) {
|
||||
const auto others = Auth().api().stickersByEmoji(original);
|
||||
if (!others) {
|
||||
return {};
|
||||
}
|
||||
result.reserve(result.size() + others->size());
|
||||
for (const auto document : *others) {
|
||||
add(document, CreateOtherSortKey(document));
|
||||
}
|
||||
}
|
||||
|
||||
ranges::action::sort(
|
||||
|
||||
@@ -63,7 +63,9 @@ public:
|
||||
Footer(not_null<StickersListWidget*> parent);
|
||||
|
||||
void preloadImages();
|
||||
void validateSelectedIcon(uint64 setId, ValidateIconAnimations animations);
|
||||
void validateSelectedIcon(
|
||||
uint64 setId,
|
||||
ValidateIconAnimations animations);
|
||||
void refreshIcons(ValidateIconAnimations animations);
|
||||
bool hasOnlyFeaturedSets() const;
|
||||
|
||||
@@ -99,6 +101,7 @@ private:
|
||||
int newSelected,
|
||||
ValidateIconAnimations animations);
|
||||
|
||||
void refreshIconsGeometry(ValidateIconAnimations animations);
|
||||
void updateSelected();
|
||||
void finishDragging();
|
||||
void paintStickerSettingsIcon(Painter &p) const;
|
||||
@@ -253,7 +256,7 @@ void StickersListWidget::Footer::enumerateVisibleIcons(Callback callback) {
|
||||
}
|
||||
|
||||
void StickersListWidget::Footer::preloadImages() {
|
||||
enumerateVisibleIcons([this](const StickerIcon &icon, int x) {
|
||||
enumerateVisibleIcons([](const StickerIcon &icon, int x) {
|
||||
if (auto sticker = icon.sticker) {
|
||||
sticker->thumb->load();
|
||||
}
|
||||
@@ -389,6 +392,7 @@ void StickersListWidget::Footer::resizeEvent(QResizeEvent *e) {
|
||||
if (_searchField) {
|
||||
resizeSearchControls();
|
||||
}
|
||||
refreshIconsGeometry(ValidateIconAnimations::None);
|
||||
}
|
||||
|
||||
void StickersListWidget::Footer::resizeSearchControls() {
|
||||
@@ -543,10 +547,11 @@ void StickersListWidget::Footer::updateSelected() {
|
||||
if (y >= _iconsTop
|
||||
&& y < _iconsTop + st::emojiFooterHeight
|
||||
&& x >= _iconsLeft
|
||||
&& x < width() - _iconsRight
|
||||
&& x < _icons.size() * st::stickerIconWidth) {
|
||||
&& x < width() - _iconsRight) {
|
||||
x += qRound(_iconsX.current()) - _iconsLeft;
|
||||
newOver = qFloor(x / st::stickerIconWidth);
|
||||
if (x < _icons.size() * st::stickerIconWidth) {
|
||||
newOver = qFloor(x / st::stickerIconWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newOver != _iconOver) {
|
||||
@@ -561,8 +566,13 @@ void StickersListWidget::Footer::updateSelected() {
|
||||
|
||||
void StickersListWidget::Footer::refreshIcons(
|
||||
ValidateIconAnimations animations) {
|
||||
_iconOver = SpecialOver::None;
|
||||
_pan->fillIcons(_icons);
|
||||
refreshIconsGeometry(animations);
|
||||
}
|
||||
|
||||
void StickersListWidget::Footer::refreshIconsGeometry(
|
||||
ValidateIconAnimations animations) {
|
||||
_iconOver = _iconDown = SpecialOver::None;
|
||||
_iconsX.finish();
|
||||
_iconSelX.finish();
|
||||
_iconsStartAnim = 0;
|
||||
@@ -919,7 +929,7 @@ void StickersListWidget::refreshSearchRows(
|
||||
_searchSets.clear();
|
||||
fillLocalSearchRows(_searchNextQuery);
|
||||
|
||||
if (!cloudSets && _searchSets.empty()) {
|
||||
if (!cloudSets && _searchNextQuery.isEmpty()) {
|
||||
showStickerSet(!_mySets.empty()
|
||||
? _mySets[0].id
|
||||
: Stickers::FeaturedSetId);
|
||||
@@ -1711,7 +1721,11 @@ void StickersListWidget::refreshSearchSets() {
|
||||
void StickersListWidget::refreshSearchIndex() {
|
||||
_searchIndex.clear();
|
||||
for (const auto &set : _mySets) {
|
||||
const auto list = TextUtilities::PrepareSearchWords(set.title + ' ' + set.shortName);
|
||||
if (set.flags & MTPDstickerSet_ClientFlag::f_special) {
|
||||
continue;
|
||||
}
|
||||
const auto string = set.title + ' ' + set.shortName;
|
||||
const auto list = TextUtilities::PrepareSearchWords(string);
|
||||
_searchIndex.emplace_back(set.id, list);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ Replace Replaces[] = {
|
||||
{ { 0xD83DDE22U }, ":'(" },
|
||||
{ { 0xD83DDE2DU }, ":_(" },
|
||||
{ { 0xD83DDE29U }, ":((" },
|
||||
{ { 0xD83DDE28U }, ":o" },
|
||||
// { { 0xD83DDE28U }, ":o" }, // Conflicts with typing :ok...
|
||||
{ { 0xD83DDE10U }, ":|" },
|
||||
{ { 0xD83DDE0CU }, "3-)" },
|
||||
{ { 0xD83DDE20U }, ">(" },
|
||||
|
||||
@@ -335,6 +335,10 @@ EmojiPtr FindReplace(const QChar *start, const QChar *end, int *outLength) {\n\
|
||||
return index ? &Items[index - 1] : nullptr;\n\
|
||||
}\n\
|
||||
\n\
|
||||
const std::vector<std::pair<QString, int>> GetReplacementPairs() {\n\
|
||||
return ReplacementPairs;\n\
|
||||
}\n\
|
||||
\n\
|
||||
EmojiPtr Find(const QChar *start, const QChar *end, int *outLength) {\n\
|
||||
auto index = FindIndex(start, end, outLength);\n\
|
||||
return index ? &Items[index - 1] : nullptr;\n\
|
||||
@@ -389,6 +393,7 @@ inline bool IsReplaceEdge(const QChar *ch) {\n\
|
||||
// return false;\n\
|
||||
}\n\
|
||||
\n\
|
||||
const std::vector<std::pair<QString, int>> GetReplacementPairs();\n\
|
||||
EmojiPtr FindReplace(const QChar *ch, const QChar *end, int *outLength = nullptr);\n\
|
||||
\n";
|
||||
header->popNamespace().stream() << "\
|
||||
@@ -591,6 +596,14 @@ EmojiPack GetSection(Section section) {\n\
|
||||
bool Generator::writeFindReplace() {
|
||||
source_->stream() << "\
|
||||
\n\
|
||||
const std::vector<std::pair<QString, int>> ReplacementPairs = {\n";
|
||||
for (const auto &[what, index] : data_.replaces) {
|
||||
source_->stream() << "\
|
||||
{ qsl(\"" << what << "\"), " << index << " },\n";
|
||||
}
|
||||
source_->stream() << "\
|
||||
};\n\
|
||||
\n\
|
||||
int FindReplaceIndex(const QChar *start, const QChar *end, int *outLength) {\n\
|
||||
auto ch = start;\n\
|
||||
\n";
|
||||
@@ -783,6 +796,7 @@ struct Replacement {\n\
|
||||
constexpr auto kReplacementMaxLength = " << maxLength << ";\n\
|
||||
\n\
|
||||
void InitReplacements();\n\
|
||||
const std::vector<Replacement> &GetAllReplacements();\n\
|
||||
const std::vector<const Replacement*> *GetReplacements(utf16char first);\n\
|
||||
utf16string GetReplacementEmoji(utf16string replacement);\n\
|
||||
\n";
|
||||
@@ -923,6 +937,10 @@ const std::vector<const Replacement*> *GetReplacements(utf16char first) {\n\
|
||||
return (it == ReplacementsMap.cend()) ? nullptr : &it->second;\n\
|
||||
}\n\
|
||||
\n\
|
||||
const std::vector<Replacement> &GetAllReplacements() {\n\
|
||||
return Replacements;\n\
|
||||
}\n\
|
||||
\n\
|
||||
utf16string GetReplacementEmoji(utf16string replacement) {\n\
|
||||
auto code = internal::countChecksum(replacement.data(), replacement.size() * sizeof(utf16char));\n\
|
||||
auto it = ReplacementsHash.find(code);\n\
|
||||
|
||||
@@ -54,7 +54,16 @@ addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
|
||||
# each key flag of parentFlags should be a subset of the value flag here
|
||||
parentFlagsCheck = {};
|
||||
|
||||
countedTypeIdExceptions = {};
|
||||
countedTypeIdExceptions[77] = countedTypeIdExceptions[78] = {}
|
||||
countedTypeIdExceptions[77]['channel'] = countedTypeIdExceptions[78]['channel'] = True
|
||||
countedTypeIdExceptions['ipPortSecret'] = True
|
||||
countedTypeIdExceptions['accessPointRule'] = True
|
||||
countedTypeIdExceptions['help_configSimple'] = True
|
||||
|
||||
lines = [];
|
||||
layer = '';
|
||||
layerIndex = 0;
|
||||
funcs = 0
|
||||
types = 0;
|
||||
consts = 0
|
||||
@@ -83,7 +92,12 @@ with open(input_file) as f:
|
||||
for line in f:
|
||||
layerline = re.match(r'// LAYER (\d+)', line)
|
||||
if (layerline):
|
||||
layer = 'constexpr auto CurrentLayer = mtpPrime(' + layerline.group(1) + ');';
|
||||
layerIndex = int(layerline.group(1));
|
||||
layer = 'constexpr auto CurrentLayer = mtpPrime(' + str(layerIndex) + ');';
|
||||
else:
|
||||
lines.append(line);
|
||||
|
||||
for line in lines:
|
||||
nocomment = re.match(r'^(.*?)//', line)
|
||||
if (nocomment):
|
||||
line = nocomment.group(1);
|
||||
@@ -131,8 +145,10 @@ with open(input_file) as f:
|
||||
if (typeid and len(typeid) > 0):
|
||||
typeid = '0x' + typeid;
|
||||
if (typeid != countTypeId):
|
||||
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + cleanline + ')');
|
||||
continue;
|
||||
if (not layerIndex in countedTypeIdExceptions or not name in countedTypeIdExceptions[layerIndex]):
|
||||
if (not name in countedTypeIdExceptions):
|
||||
print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + cleanline + ')');
|
||||
continue;
|
||||
else:
|
||||
typeid = countTypeId;
|
||||
|
||||
@@ -507,7 +523,9 @@ def addTextSerialize(lst, dct, dataLetter):
|
||||
if (not vtypeget):
|
||||
result += '); vtypes.push_back(0';
|
||||
else:
|
||||
result += '0); vtypes.push_back(0';
|
||||
if (not vtypeget):
|
||||
result += '0';
|
||||
result += '); vtypes.push_back(0';
|
||||
result += '); stages.push_back(0); flags.push_back(0); ';
|
||||
if (k in conditions):
|
||||
result += '} else { to.add("[ SKIPPED BY BIT ' + conditions[k] + ' IN FIELD ' + hasFlags + ' ]"); } ';
|
||||
|
||||
@@ -457,7 +457,7 @@ public:\n\
|
||||
inline const color &get_transparent() const { return _colors[0]; }; // special color\n";
|
||||
|
||||
int indexInPalette = 1;
|
||||
if (!module_.enumVariables([this, &indexInPalette](const Variable &variable) -> bool {
|
||||
if (!module_.enumVariables([&](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
if (variable.value.type().tag != structure::TypeTag::Color) {
|
||||
return false;
|
||||
@@ -584,7 +584,7 @@ QList<row> data();\n\
|
||||
}
|
||||
|
||||
bool Generator::writeStructsForwardDeclarations() {
|
||||
bool hasNoExternalStructs = module_.enumVariables([this](const Variable &value) -> bool {
|
||||
bool hasNoExternalStructs = module_.enumVariables([&](const Variable &value) -> bool {
|
||||
if (value.value.type().tag == structure::TypeTag::Struct) {
|
||||
if (!module_.findStructInModule(value.value.type().name, module_)) {
|
||||
return false;
|
||||
@@ -598,7 +598,7 @@ bool Generator::writeStructsForwardDeclarations() {
|
||||
|
||||
header_->newline();
|
||||
std::set<QString> alreadyDeclaredTypes;
|
||||
bool result = module_.enumVariables([this, &alreadyDeclaredTypes](const Variable &value) -> bool {
|
||||
bool result = module_.enumVariables([&](const Variable &value) -> bool {
|
||||
if (value.value.type().tag == structure::TypeTag::Struct) {
|
||||
if (!module_.findStructInModule(value.value.type().name, module_)) {
|
||||
if (alreadyDeclaredTypes.find(value.value.type().name.back()) == alreadyDeclaredTypes.end()) {
|
||||
@@ -618,7 +618,7 @@ bool Generator::writeStructsDefinitions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = module_.enumStructs([this](const Struct &value) -> bool {
|
||||
bool result = module_.enumStructs([&](const Struct &value) -> bool {
|
||||
header_->stream() << "\
|
||||
struct " << value.name.back() << " {\n";
|
||||
for (auto &field : value.fields) {
|
||||
@@ -646,7 +646,7 @@ bool Generator::writeRefsDeclarations() {
|
||||
if (isPalette_) {
|
||||
header_->stream() << "extern const style::color &transparent; // special color\n";
|
||||
}
|
||||
bool result = module_.enumVariables([this](const Variable &value) -> bool {
|
||||
bool result = module_.enumVariables([&](const Variable &value) -> bool {
|
||||
auto name = value.name.back();
|
||||
auto type = typeToString(value.value.type());
|
||||
if (type.isEmpty()) {
|
||||
@@ -668,7 +668,7 @@ bool Generator::writeIncludesInSource() {
|
||||
}
|
||||
|
||||
auto includes = QStringList();
|
||||
std::function<bool(const Module&)> collector = [this, &collector, &includes](const Module &module) {
|
||||
std::function<bool(const Module&)> collector = [&](const Module &module) {
|
||||
module.enumIncludes(collector);
|
||||
auto base = moduleBaseName(module);
|
||||
if (!includes.contains(base)) {
|
||||
@@ -690,7 +690,7 @@ bool Generator::writeVariableDefinitions() {
|
||||
}
|
||||
|
||||
source_->newline();
|
||||
bool result = module_.enumVariables([this](const Variable &variable) -> bool {
|
||||
bool result = module_.enumVariables([&](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto type = typeToString(variable.value.type());
|
||||
if (type.isEmpty()) {
|
||||
@@ -710,7 +710,7 @@ bool Generator::writeRefsDefinition() {
|
||||
if (isPalette_) {
|
||||
source_->stream() << "const style::color &transparent(_palette.get_transparent()); // special color\n";
|
||||
}
|
||||
bool result = module_.enumVariables([this](const Variable &variable) -> bool {
|
||||
bool result = module_.enumVariables([&](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto type = typeToString(variable.value.type());
|
||||
if (type.isEmpty()) {
|
||||
@@ -752,7 +752,7 @@ void palette::finalize() {\n\
|
||||
compute(0, -1, { 255, 255, 255, 0}); // special color\n";
|
||||
|
||||
QList<structure::FullName> names;
|
||||
module_.enumVariables([this, &names](const Variable &variable) -> bool {
|
||||
module_.enumVariables([&](const Variable &variable) -> bool {
|
||||
names.push_back(variable.name);
|
||||
return true;
|
||||
});
|
||||
@@ -761,7 +761,7 @@ void palette::finalize() {\n\
|
||||
int indexInPalette = 1;
|
||||
QByteArray checksumString;
|
||||
checksumString.append("&transparent:{ 255, 255, 255, 0 }");
|
||||
auto result = module_.enumVariables([this, &indexInPalette, &checksumString, &dataRows, &names](const Variable &variable) -> bool {
|
||||
auto result = module_.enumVariables([&](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto index = indexInPalette++;
|
||||
paletteIndices_.emplace(name, index);
|
||||
@@ -826,7 +826,7 @@ int getPaletteIndex(QLatin1String name) {\n\
|
||||
auto tabsUsed = 1;
|
||||
|
||||
// Returns true if at least one check was finished.
|
||||
auto finishChecksTillKey = [this, &chars, &checkTypes, &checkLengthHistory, &tabsUsed, tabs](const QString &key) {
|
||||
auto finishChecksTillKey = [&](const QString &key) {
|
||||
auto result = false;
|
||||
while (!chars.isEmpty() && key.midRef(0, chars.size()) != chars) {
|
||||
result = true;
|
||||
@@ -1069,7 +1069,7 @@ void init_" << baseName_ << "() {\n\
|
||||
|
||||
if (module_.hasIncludes()) {
|
||||
bool writtenAtLeastOne = false;
|
||||
bool result = module_.enumIncludes([this,&writtenAtLeastOne](const Module &module) -> bool {
|
||||
bool result = module_.enumIncludes([&](const Module &module) -> bool {
|
||||
if (module.hasVariables()) {
|
||||
source_->stream() << "\tinit_" + moduleBaseName(module) + "();\n";
|
||||
writtenAtLeastOne = true;
|
||||
@@ -1096,7 +1096,7 @@ void init_" << baseName_ << "() {\n\
|
||||
|
||||
if (isPalette_) {
|
||||
source_->stream() << "\t_palette.finalize();\n";
|
||||
} else if (!module_.enumVariables([this](const Variable &variable) -> bool {
|
||||
} else if (!module_.enumVariables([&](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto value = valueAssignmentCode(variable.value);
|
||||
if (value.isEmpty()) {
|
||||
|
||||
@@ -32,6 +32,7 @@ constexpr int kErrorAlreadyDefined = 805;
|
||||
constexpr int kErrorBadString = 806;
|
||||
constexpr int kErrorIconDuplicate = 807;
|
||||
constexpr int kErrorBadIconModifier = 808;
|
||||
constexpr int kErrorCyclicDependency = 809;
|
||||
|
||||
QString findInputFile(const Options &options) {
|
||||
for (const auto &dir : options.includePaths) {
|
||||
@@ -148,14 +149,21 @@ Modifier GetModifier(const QString &name) {
|
||||
return modifiers.value(name);
|
||||
}
|
||||
|
||||
ParsedFile::ParsedFile(const Options &options)
|
||||
ParsedFile::ParsedFile(
|
||||
const Options &options,
|
||||
std::vector<QString> includeStack)
|
||||
: filePath_(findInputFile(options))
|
||||
, file_(filePath_)
|
||||
, options_(options) {
|
||||
, options_(options)
|
||||
, includeStack_(includeStack) {
|
||||
}
|
||||
|
||||
bool ParsedFile::read() {
|
||||
if (!file_.read()) {
|
||||
if (std::find(begin(includeStack_), end(includeStack_), filePath_)
|
||||
!= end(includeStack_)) {
|
||||
logError(kErrorCyclicDependency) << "include cycle detected.";
|
||||
return false;
|
||||
} else if (!file_.read()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -205,7 +213,11 @@ common::LogStream ParsedFile::logErrorTypeMismatch() {
|
||||
ParsedFile::ModulePtr ParsedFile::readIncluded() {
|
||||
if (auto usingFile = assertNextToken(BasicType::String)) {
|
||||
if (assertNextToken(BasicType::Semicolon)) {
|
||||
ParsedFile included(includedOptions(tokenValue(usingFile)));
|
||||
auto includeStack = includeStack_;
|
||||
includeStack.push_back(filePath_);
|
||||
ParsedFile included(
|
||||
includedOptions(tokenValue(usingFile)),
|
||||
includeStack);
|
||||
if (included.read()) {
|
||||
return included.getResult();
|
||||
} else {
|
||||
|
||||
@@ -24,7 +24,9 @@ Modifier GetModifier(const QString &name);
|
||||
// Parses an input file to the internal struct.
|
||||
class ParsedFile {
|
||||
public:
|
||||
explicit ParsedFile(const Options &options);
|
||||
explicit ParsedFile(
|
||||
const Options &options,
|
||||
std::vector<QString> includeStack = {});
|
||||
ParsedFile(const ParsedFile &other) = delete;
|
||||
ParsedFile &operator=(const ParsedFile &other) = delete;
|
||||
|
||||
@@ -108,6 +110,8 @@ private:
|
||||
bool failed_ = false;
|
||||
ModulePtr module_;
|
||||
|
||||
std::vector<QString> includeStack_;
|
||||
|
||||
QMap<std::string, structure::Type> typeNames_ = {
|
||||
{ "int" , { structure::TypeTag::Int } },
|
||||
{ "double" , { structure::TypeTag::Double } },
|
||||
|
||||
@@ -25,13 +25,6 @@ enum {
|
||||
MTPAckSendWaiting = 10000, // how much time to wait for some more requests, when sending msg acks
|
||||
MTPResendThreshold = 1, // how much ints should message contain for us not to resend, but to check it's state
|
||||
MTPContainerLives = 600, // container lives 10 minutes in haveSent map
|
||||
MTPMinReceiveDelay = 4000, // 4 seconds
|
||||
MTPMaxReceiveDelay = 64000, // 64 seconds
|
||||
MTPMinConnectDelay = 1000, // tcp connect should take less then 1 second
|
||||
MTPMaxConnectDelay = 8000, // tcp connect should take 8 seconds max
|
||||
MTPConnectionOldTimeout = 192000, // 192 seconds
|
||||
MTPTcpConnectionWaitTimeout = 2000, // 2 seconds waiting for tcp, until we accept http
|
||||
MTPIPv4ConnectionWaitTimeout = 1000, // 1 seconds waiting for ipv4, until we accept ipv6
|
||||
|
||||
MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill
|
||||
|
||||
@@ -39,10 +32,6 @@ enum {
|
||||
|
||||
MaxUsersPerInvite = 100, // max users in one super group invite request
|
||||
|
||||
MTPPingDelayDisconnect = 60, // 1 min
|
||||
MTPPingSendAfterAuto = 30, // send new ping starting from 30 seconds (add to existing container)
|
||||
MTPPingSendAfter = 45, // send new ping after 45 seconds without ping
|
||||
|
||||
MTPChannelGetDifferenceLimit = 100,
|
||||
|
||||
MaxSelectedItems = 100,
|
||||
@@ -163,11 +152,11 @@ static const BuiltInDc _builtInDcs[] = {
|
||||
};
|
||||
|
||||
static const BuiltInDc _builtInDcsIPv6[] = {
|
||||
{ 1, "2001:b28:f23d:f001::a", 443 },
|
||||
{ 2, "2001:67c:4e8:f002::a", 443 },
|
||||
{ 3, "2001:b28:f23d:f003::a", 443 },
|
||||
{ 4, "2001:67c:4e8:f004::a", 443 },
|
||||
{ 5, "2001:b28:f23f:f005::a", 443 }
|
||||
{ 1, "2001:0b28:f23d:f001:0000:0000:0000:000a", 443 },
|
||||
{ 2, "2001:067c:04e8:f002:0000:0000:0000:000a", 443 },
|
||||
{ 3, "2001:0b28:f23d:f003:0000:0000:0000:000a", 443 },
|
||||
{ 4, "2001:067c:04e8:f004:0000:0000:0000:000a", 443 },
|
||||
{ 5, "2001:0b28:f23f:f005:0000:0000:0000:000a", 443 }
|
||||
};
|
||||
|
||||
static const BuiltInDc _builtInTestDcs[] = {
|
||||
@@ -177,9 +166,9 @@ static const BuiltInDc _builtInTestDcs[] = {
|
||||
};
|
||||
|
||||
static const BuiltInDc _builtInTestDcsIPv6[] = {
|
||||
{ 1, "2001:b28:f23d:f001::e", 443 },
|
||||
{ 2, "2001:67c:4e8:f002::e", 443 },
|
||||
{ 3, "2001:b28:f23d:f003::e", 443 }
|
||||
{ 1, "2001:0b28:f23d:f001:0000:0000:0000:000e", 443 },
|
||||
{ 2, "2001:067c:04e8:f002:0000:0000:0000:000e", 443 },
|
||||
{ 3, "2001:0b28:f23d:f003:0000:0000:0000:000e", 443 }
|
||||
};
|
||||
|
||||
inline const BuiltInDc *builtInDcs() {
|
||||
|
||||
@@ -18,31 +18,6 @@ namespace {
|
||||
|
||||
std::map<int, const char*> AlphaLogs() {
|
||||
return {
|
||||
{
|
||||
1001024,
|
||||
"\xE2\x80\x94 Radically improved navigation. "
|
||||
"New side panel on the right with quick access to "
|
||||
"shared media and group members.\n"
|
||||
|
||||
"\xE2\x80\x94 Pinned Messages. If you are a channel admin, "
|
||||
"pin messages to focus your subscribers\xE2\x80\x99 attention "
|
||||
"on important announcements.\n"
|
||||
|
||||
"\xE2\x80\x94 Also supported clearing history in supergroups "
|
||||
"and added a host of minor improvements."
|
||||
},
|
||||
{
|
||||
1001026,
|
||||
"\xE2\x80\x94 Admin badges in supergroup messages.\n"
|
||||
"\xE2\x80\x94 Fix crashing on launch in OS X 10.6.\n"
|
||||
"\xE2\x80\x94 Bug fixes and other minor improvements."
|
||||
},
|
||||
{
|
||||
1001027,
|
||||
"\xE2\x80\x94 Saved Messages. Bookmark messages by forwarding them "
|
||||
"to \xE2\x80\x9C""Saved Messages\xE2\x80\x9D. "
|
||||
"Access them from the Chats list or from the side menu."
|
||||
},
|
||||
{
|
||||
1002002,
|
||||
"\xE2\x80\x94 Grouped photos and videos are displayed as albums."
|
||||
@@ -80,6 +55,25 @@ std::map<int, const char*> AlphaLogs() {
|
||||
"\xE2\x80\x94 Search for Stickers. "
|
||||
"Click on the new search icon to access "
|
||||
"your sticker sets or find new ones."
|
||||
},
|
||||
{
|
||||
1002019,
|
||||
"\xE2\x80\x94 Enable proxy for calls in Settings.\n"
|
||||
|
||||
"\xE2\x80\x94 Bug fixes and other minor improvements."
|
||||
},
|
||||
{
|
||||
1002020,
|
||||
"\xE2\x80\x94 Emoji and text replacements are done "
|
||||
"while you type the message.\n"
|
||||
|
||||
"\xE2\x80\x94 Revert emoji and text replacements "
|
||||
"by pressing backspace.\n"
|
||||
|
||||
"\xE2\x80\x94 Disable emoji replacements or suggestions "
|
||||
"in Settings.\n"
|
||||
|
||||
"\xE2\x80\x94 Some critical bug fixes."
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ QString tryConvertUrlToLocal(QString url) {
|
||||
return url;
|
||||
} else if (auto socksMatch = regex_match(qsl("socks/?\\?(.+)(#|$)"), query, matchOptions)) {
|
||||
return qsl("tg://socks?") + socksMatch->captured(1);
|
||||
} else if (auto proxyMatch = regex_match(qsl("proxy/?\\?(.+)(#|$)"), query, matchOptions)) {
|
||||
return qsl("tg://proxy?") + proxyMatch->captured(1);
|
||||
} else if (auto usernameMatch = regex_match(qsl("^([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), query, matchOptions)) {
|
||||
auto params = query.mid(usernameMatch->captured(0).size()).toString();
|
||||
auto postParam = QString();
|
||||
|
||||
@@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "platform/platform_specific.h"
|
||||
#include "application.h"
|
||||
#include "base/zlib_help.h"
|
||||
#include "autoupdater.h"
|
||||
#include "core/update_checker.h"
|
||||
|
||||
PreLaunchWindow *PreLaunchWindowInstance = nullptr;
|
||||
|
||||
@@ -299,23 +299,36 @@ LastCrashedWindow::LastCrashedWindow()
|
||||
_updatingSkip.setText(qsl("SKIP"));
|
||||
connect(&_updatingSkip, SIGNAL(clicked()), this, SLOT(onUpdateSkip()));
|
||||
|
||||
Sandbox::connect(SIGNAL(updateChecking()), this, SLOT(onUpdateChecking()));
|
||||
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onUpdateLatest()));
|
||||
Sandbox::connect(SIGNAL(updateProgress(qint64,qint64)), this, SLOT(onUpdateDownloading(qint64,qint64)));
|
||||
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
|
||||
Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
|
||||
Core::UpdateChecker checker;
|
||||
using Progress = Core::UpdateChecker::Progress;
|
||||
checker.checking(
|
||||
) | rpl::start_with_next([=] { onUpdateChecking(); }, _lifetime);
|
||||
checker.isLatest(
|
||||
) | rpl::start_with_next([=] { onUpdateLatest(); }, _lifetime);
|
||||
checker.progress(
|
||||
) | rpl::start_with_next([=](const Progress &result) {
|
||||
onUpdateDownloading(result.already, result.size);
|
||||
}, _lifetime);
|
||||
checker.failed(
|
||||
) | rpl::start_with_next([=] { onUpdateFailed(); }, _lifetime);
|
||||
checker.ready(
|
||||
) | rpl::start_with_next([=] { onUpdateReady(); }, _lifetime);
|
||||
|
||||
switch (Sandbox::updatingState()) {
|
||||
case Application::UpdatingDownload:
|
||||
switch (checker.state()) {
|
||||
case Core::UpdateChecker::State::Download:
|
||||
setUpdatingState(UpdatingDownload, true);
|
||||
setDownloadProgress(Sandbox::updatingReady(), Sandbox::updatingSize());
|
||||
break;
|
||||
case Application::UpdatingReady: setUpdatingState(UpdatingReady, true); break;
|
||||
default: setUpdatingState(UpdatingCheck, true); break;
|
||||
setDownloadProgress(checker.already(), checker.size());
|
||||
break;
|
||||
case Core::UpdateChecker::State::Ready:
|
||||
setUpdatingState(UpdatingReady, true);
|
||||
break;
|
||||
default:
|
||||
setUpdatingState(UpdatingCheck, true);
|
||||
break;
|
||||
}
|
||||
|
||||
cSetLastUpdateCheck(0);
|
||||
Sandbox::startUpdateCheck();
|
||||
checker.start();
|
||||
#else // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
_updating.setText(qsl("Please check if there is a new version available."));
|
||||
if (_sendingState != SendingNoReport) {
|
||||
@@ -434,7 +447,6 @@ void LastCrashedWindow::onSendReport() {
|
||||
_sendReply->deleteLater();
|
||||
_sendReply = nullptr;
|
||||
}
|
||||
App::setProxySettings(_sendManager);
|
||||
|
||||
QString apiid = getReportField(qstr("apiid"), qstr("ApiId:")), version = getReportField(qstr("version"), qstr("Version:"));
|
||||
_checkReply = _sendManager.get(QNetworkRequest(qsl("https://tdesktop.com/crash.php?act=query_report&apiid=%1&version=%2&dmp=%3&platform=%4").arg(apiid).arg(version).arg(minidumpFileName().isEmpty() ? 0 : 1).arg(cPlatformString())));
|
||||
@@ -756,22 +768,45 @@ void LastCrashedWindow::updateControls() {
|
||||
}
|
||||
|
||||
void LastCrashedWindow::onNetworkSettings() {
|
||||
auto &p = Sandbox::PreLaunchProxy();
|
||||
NetworkSettingsWindow *box = new NetworkSettingsWindow(this, p.host, p.port ? p.port : 80, p.user, p.password);
|
||||
connect(box, SIGNAL(saved(QString, quint32, QString, QString)), this, SLOT(onNetworkSettingsSaved(QString, quint32, QString, QString)));
|
||||
const auto &proxy = Sandbox::PreLaunchProxy();
|
||||
const auto box = new NetworkSettingsWindow(
|
||||
this,
|
||||
proxy.host,
|
||||
proxy.port ? proxy.port : 80,
|
||||
proxy.user,
|
||||
proxy.password);
|
||||
connect(
|
||||
box,
|
||||
SIGNAL(saved(QString,quint32,QString,QString)),
|
||||
this,
|
||||
SLOT(onNetworkSettingsSaved(QString,quint32,QString,QString)));
|
||||
box->show();
|
||||
}
|
||||
|
||||
void LastCrashedWindow::onNetworkSettingsSaved(QString host, quint32 port, QString username, QString password) {
|
||||
Sandbox::RefPreLaunchProxy().host = host;
|
||||
Sandbox::RefPreLaunchProxy().port = port ? port : 80;
|
||||
Sandbox::RefPreLaunchProxy().user = username;
|
||||
Sandbox::RefPreLaunchProxy().password = password;
|
||||
void LastCrashedWindow::onNetworkSettingsSaved(
|
||||
QString host,
|
||||
quint32 port,
|
||||
QString username,
|
||||
QString password) {
|
||||
Expects(host.isEmpty() || port != 0);
|
||||
|
||||
auto &proxy = Sandbox::RefPreLaunchProxy();
|
||||
proxy.type = host.isEmpty()
|
||||
? ProxyData::Type::None
|
||||
: ProxyData::Type::Http;
|
||||
proxy.host = host;
|
||||
proxy.port = port;
|
||||
proxy.user = username;
|
||||
proxy.password = password;
|
||||
|
||||
Sandbox::refreshGlobalProxy();
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
if ((_updatingState == UpdatingFail && (_sendingState == SendingNoReport || _sendingState == SendingUpdateCheck)) || (_updatingState == UpdatingCheck)) {
|
||||
Sandbox::stopUpdate();
|
||||
Core::UpdateChecker checker;
|
||||
checker.stop();
|
||||
cSetLastUpdateCheck(0);
|
||||
Sandbox::startUpdateCheck();
|
||||
checker.start();
|
||||
} else
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
if (_sendingState == SendingFail || _sendingState == SendingProgress) {
|
||||
@@ -794,7 +829,7 @@ void LastCrashedWindow::setUpdatingState(UpdatingState state, bool force) {
|
||||
}
|
||||
break;
|
||||
case UpdatingReady:
|
||||
if (checkReadyUpdate()) {
|
||||
if (Core::checkReadyUpdate()) {
|
||||
cSetRestartingUpdate(true);
|
||||
App::quit();
|
||||
return;
|
||||
@@ -828,15 +863,18 @@ void LastCrashedWindow::setDownloadProgress(qint64 ready, qint64 total) {
|
||||
|
||||
void LastCrashedWindow::onUpdateRetry() {
|
||||
cSetLastUpdateCheck(0);
|
||||
Sandbox::startUpdateCheck();
|
||||
Core::UpdateChecker checker;
|
||||
checker.start();
|
||||
}
|
||||
|
||||
void LastCrashedWindow::onUpdateSkip() {
|
||||
if (_sendingState == SendingNoReport) {
|
||||
onContinue();
|
||||
} else {
|
||||
if (_updatingState == UpdatingCheck || _updatingState == UpdatingDownload) {
|
||||
Sandbox::stopUpdate();
|
||||
if (_updatingState == UpdatingCheck
|
||||
|| _updatingState == UpdatingDownload) {
|
||||
Core::UpdateChecker checker;
|
||||
checker.stop();
|
||||
setUpdatingState(UpdatingFail);
|
||||
}
|
||||
_sendingState = SendingNone;
|
||||
|
||||
@@ -114,6 +114,11 @@ private:
|
||||
QString minidumpFileName();
|
||||
void updateControls();
|
||||
|
||||
void excludeReportUsername();
|
||||
|
||||
QString getReportField(const QLatin1String &name, const QLatin1String &prefix);
|
||||
void addReportFieldPart(const QLatin1String &name, const QLatin1String &prefix, QHttpMultiPart *multipart);
|
||||
|
||||
QString _host, _username, _password;
|
||||
quint32 _port;
|
||||
|
||||
@@ -128,8 +133,6 @@ private:
|
||||
|
||||
bool _reportShown, _reportSaved;
|
||||
|
||||
void excludeReportUsername();
|
||||
|
||||
enum SendingState {
|
||||
SendingNoReport,
|
||||
SendingUpdateCheck,
|
||||
@@ -167,8 +170,7 @@ private:
|
||||
void setDownloadProgress(qint64 ready, qint64 total);
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
QString getReportField(const QLatin1String &name, const QLatin1String &prefix);
|
||||
void addReportFieldPart(const QLatin1String &name, const QLatin1String &prefix, QHttpMultiPart *multipart);
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -545,7 +545,11 @@ const dump &operator<<(const dump &stream, const wchar_t *str) {
|
||||
if (!ReportFile) return stream;
|
||||
|
||||
for (int i = 0, l = wcslen(str); i < l; ++i) {
|
||||
if (str[i] >= 0 && str[i] < 128) {
|
||||
if (
|
||||
#if !defined(__WCHAR_UNSIGNED__)
|
||||
str[i] >= 0 &&
|
||||
#endif
|
||||
str[i] < 128) {
|
||||
SafeWriteChar(char(str[i]));
|
||||
} else {
|
||||
SafeWriteChar('?');
|
||||
|
||||
@@ -109,7 +109,6 @@ void Launcher::prepareSettings() {
|
||||
|
||||
switch (cPlatform()) {
|
||||
case dbipWindows:
|
||||
gUpdateURL = QUrl(qsl("http://tdesktop.com/win/tupdates/current"));
|
||||
#ifndef OS_WIN_STORE
|
||||
gPlatformString = qsl("Windows");
|
||||
#else // OS_WIN_STORE
|
||||
@@ -117,7 +116,6 @@ void Launcher::prepareSettings() {
|
||||
#endif // OS_WIN_STORE
|
||||
break;
|
||||
case dbipMac:
|
||||
gUpdateURL = QUrl(qsl("http://tdesktop.com/mac/tupdates/current"));
|
||||
#ifndef OS_MAC_STORE
|
||||
gPlatformString = qsl("MacOS");
|
||||
#else // OS_MAC_STORE
|
||||
@@ -125,15 +123,12 @@ void Launcher::prepareSettings() {
|
||||
#endif // OS_MAC_STORE
|
||||
break;
|
||||
case dbipMacOld:
|
||||
gUpdateURL = QUrl(qsl("http://tdesktop.com/mac32/tupdates/current"));
|
||||
gPlatformString = qsl("MacOSold");
|
||||
break;
|
||||
case dbipLinux64:
|
||||
gUpdateURL = QUrl(qsl("http://tdesktop.com/linux/tupdates/current"));
|
||||
gPlatformString = qsl("Linux64bit");
|
||||
break;
|
||||
case dbipLinux32:
|
||||
gUpdateURL = QUrl(qsl("http://tdesktop.com/linux32/tupdates/current"));
|
||||
gPlatformString = qsl("Linux32bit");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ public:
|
||||
return _customWorkingDir;
|
||||
}
|
||||
|
||||
virtual ~Launcher() = default;
|
||||
|
||||
protected:
|
||||
enum class UpdaterLaunch {
|
||||
PerformUpdate,
|
||||
|
||||
1192
Telegram/SourceFiles/core/update_checker.cpp
Normal file
59
Telegram/SourceFiles/core/update_checker.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
class Updater;
|
||||
|
||||
class UpdateChecker {
|
||||
public:
|
||||
enum class State {
|
||||
None,
|
||||
Download,
|
||||
Ready,
|
||||
};
|
||||
struct Progress {
|
||||
int64 already;
|
||||
int64 size;
|
||||
};
|
||||
|
||||
UpdateChecker();
|
||||
|
||||
rpl::producer<> checking() const;
|
||||
rpl::producer<> isLatest() const;
|
||||
rpl::producer<Progress> progress() const;
|
||||
rpl::producer<> failed() const;
|
||||
rpl::producer<> ready() const;
|
||||
|
||||
void start(bool forceWait = false);
|
||||
void stop();
|
||||
void test();
|
||||
|
||||
State state() const;
|
||||
int already() const;
|
||||
int size() const;
|
||||
|
||||
private:
|
||||
const std::shared_ptr<Updater> _updater;
|
||||
|
||||
};
|
||||
|
||||
bool checkReadyUpdate();
|
||||
|
||||
#else // TDESKTOP_DISABLE_AUTOUPDATE
|
||||
class UpdateChecker {
|
||||
};
|
||||
|
||||
#endif // TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
QString countBetaVersionSignature(uint64 version);
|
||||
|
||||
} // namespace Core
|
||||
@@ -236,6 +236,57 @@ namespace {
|
||||
_MsStarter _msStarter;
|
||||
}
|
||||
|
||||
bool ProxyData::valid() const {
|
||||
if (type == Type::None || host.isEmpty() || !port) {
|
||||
return false;
|
||||
} else if (type == Type::Mtproto && !ValidSecret(password)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProxyData::supportsCalls() const {
|
||||
return (type == Type::Socks5);
|
||||
}
|
||||
|
||||
ProxyData::operator bool() const {
|
||||
return valid();
|
||||
}
|
||||
|
||||
bool ProxyData::operator==(const ProxyData &other) const {
|
||||
if (!valid()) {
|
||||
return !other.valid();
|
||||
}
|
||||
return (type == other.type)
|
||||
&& (host == other.host)
|
||||
&& (port == other.port)
|
||||
&& (user == other.user)
|
||||
&& (password == other.password);
|
||||
}
|
||||
|
||||
bool ProxyData::operator!=(const ProxyData &other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool ProxyData::ValidSecret(const QString &secret) {
|
||||
return QRegularExpression("^[a-fA-F0-9]{32}$").match(secret).hasMatch();
|
||||
}
|
||||
|
||||
QNetworkProxy ToNetworkProxy(const ProxyData &proxy) {
|
||||
if (proxy.type == ProxyData::Type::None
|
||||
|| proxy.type == ProxyData::Type::Mtproto) {
|
||||
return QNetworkProxy::NoProxy;
|
||||
}
|
||||
return QNetworkProxy(
|
||||
(proxy.type == ProxyData::Type::Socks5
|
||||
? QNetworkProxy::Socks5Proxy
|
||||
: QNetworkProxy::HttpProxy),
|
||||
proxy.host,
|
||||
proxy.port,
|
||||
proxy.user,
|
||||
proxy.password);
|
||||
}
|
||||
|
||||
namespace ThirdParty {
|
||||
|
||||
void start() {
|
||||
@@ -289,7 +340,9 @@ namespace ThirdParty {
|
||||
av_lockmgr_register(nullptr);
|
||||
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
FIPS_mode_set(0);
|
||||
#endif
|
||||
ENGINE_cleanup();
|
||||
CONF_modules_unload(1);
|
||||
ERR_remove_state(0);
|
||||
|
||||
@@ -420,19 +420,31 @@ enum DBIWorkMode {
|
||||
dbiwmWindowOnly = 2,
|
||||
};
|
||||
|
||||
enum DBIConnectionType {
|
||||
dbictAuto = 0,
|
||||
dbictHttpAuto = 1, // not used
|
||||
dbictHttpProxy = 2,
|
||||
dbictTcpProxy = 3,
|
||||
};
|
||||
|
||||
struct ProxyData {
|
||||
enum class Type {
|
||||
None,
|
||||
Socks5,
|
||||
Http,
|
||||
Mtproto,
|
||||
};
|
||||
|
||||
Type type = Type::None;
|
||||
QString host;
|
||||
uint32 port = 0;
|
||||
QString user, password;
|
||||
|
||||
bool valid() const;
|
||||
bool supportsCalls() const;
|
||||
explicit operator bool() const;
|
||||
bool operator==(const ProxyData &other) const;
|
||||
bool operator!=(const ProxyData &other) const;
|
||||
|
||||
static bool ValidSecret(const QString &secret);
|
||||
|
||||
};
|
||||
|
||||
QNetworkProxy ToNetworkProxy(const ProxyData &proxy);
|
||||
|
||||
enum DBIScale {
|
||||
dbisAuto = 0,
|
||||
dbisOne = 1,
|
||||
|
||||
@@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#define BETA_VERSION_MACRO (0ULL)
|
||||
|
||||
constexpr int AppVersion = 1002010;
|
||||
constexpr str_const AppVersionStr = "1.2.10";
|
||||
constexpr int AppVersion = 1002020;
|
||||
constexpr str_const AppVersionStr = "1.2.20";
|
||||
constexpr bool AppAlphaVersion = true;
|
||||
constexpr uint64 AppBetaVersion = BETA_VERSION_MACRO;
|
||||
|
||||
@@ -205,6 +205,10 @@ bool Media::consumeMessageText(const TextWithEntities &text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TextWithEntities Media::consumedMessageText() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<HistoryMedia> Media::createView(
|
||||
not_null<HistoryView::Element*> message) {
|
||||
return createView(message, message->data());
|
||||
@@ -482,8 +486,9 @@ QString MediaFile::chatsListText() const {
|
||||
return lang(lng_in_dlg_video);
|
||||
} else if (_document->isVoiceMessage()) {
|
||||
return lang(lng_in_dlg_audio);
|
||||
} else if (!_document->filename().isEmpty()) {
|
||||
return _document->filename();
|
||||
} else if (const auto name = _document->composeNameString();
|
||||
!name.isEmpty()) {
|
||||
return name;
|
||||
} else if (_document->isAudioFile()) {
|
||||
return lang(lng_in_dlg_audio_file);
|
||||
}
|
||||
@@ -907,6 +912,14 @@ std::unique_ptr<Media> MediaWebPage::clone(not_null<HistoryItem*> parent) {
|
||||
return std::make_unique<MediaWebPage>(parent, _page);
|
||||
}
|
||||
|
||||
DocumentData *MediaWebPage::document() const {
|
||||
return _page->document;
|
||||
}
|
||||
|
||||
PhotoData *MediaWebPage::photo() const {
|
||||
return _page->photo;
|
||||
}
|
||||
|
||||
WebPageData *MediaWebPage::webpage() const {
|
||||
return _page;
|
||||
}
|
||||
@@ -1032,6 +1045,10 @@ bool MediaGame::consumeMessageText(const TextWithEntities &text) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TextWithEntities MediaGame::consumedMessageText() const {
|
||||
return _consumedText;
|
||||
}
|
||||
|
||||
bool MediaGame::updateInlineResultMedia(const MTPMessageMedia &media) {
|
||||
return updateSentMedia(media);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class enum_mask;
|
||||
} // namespace base
|
||||
|
||||
namespace Storage {
|
||||
enum class SharedMediaType : char;
|
||||
enum class SharedMediaType : signed char;
|
||||
using SharedMediaTypesMask = base::enum_mask<SharedMediaType>;
|
||||
} // namespace Storage
|
||||
|
||||
@@ -100,6 +100,7 @@ public:
|
||||
|
||||
[[nodiscard]] virtual bool consumeMessageText(
|
||||
const TextWithEntities &text);
|
||||
[[nodiscard]] virtual TextWithEntities consumedMessageText() const;
|
||||
|
||||
// After sending an inline result we may want to completely recreate
|
||||
// the media (all media that was generated on client side, for example).
|
||||
@@ -292,6 +293,8 @@ public:
|
||||
|
||||
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
|
||||
|
||||
DocumentData *document() const override;
|
||||
PhotoData *photo() const override;
|
||||
WebPageData *webpage() const override;
|
||||
|
||||
bool hasReplyPreview() const override;
|
||||
@@ -332,6 +335,7 @@ public:
|
||||
not_null<ChannelData*> channel) const override;
|
||||
|
||||
bool consumeMessageText(const TextWithEntities &text) override;
|
||||
TextWithEntities consumedMessageText() const override;
|
||||
|
||||
bool updateInlineResultMedia(const MTPMessageMedia &media) override;
|
||||
bool updateSentMedia(const MTPMessageMedia &media) override;
|
||||
|
||||
@@ -114,28 +114,15 @@ MTPinputPeerNotifySettings NotifySettingsValue::serialize() const {
|
||||
}
|
||||
|
||||
bool NotifySettings::change(const MTPPeerNotifySettings &settings) {
|
||||
switch (settings.type()) {
|
||||
case mtpc_peerNotifySettingsEmpty: {
|
||||
if (!_known || _value) {
|
||||
_known = true;
|
||||
_value = nullptr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} break;
|
||||
Expects(settings.type() == mtpc_peerNotifySettings);
|
||||
|
||||
case mtpc_peerNotifySettings: {
|
||||
auto &data = settings.c_peerNotifySettings();
|
||||
if (_value) {
|
||||
return _value->change(data);
|
||||
}
|
||||
_known = true;
|
||||
_value = std::make_unique<NotifySettingsValue>(data);
|
||||
return true;
|
||||
} break;
|
||||
auto &data = settings.c_peerNotifySettings();
|
||||
if (_value) {
|
||||
return _value->change(data);
|
||||
}
|
||||
|
||||
Unexpected("Type in NotifySettings::change()");
|
||||
_known = true;
|
||||
_value = std::make_unique<NotifySettingsValue>(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
NotifySettings::NotifySettings() = default;
|
||||
@@ -152,18 +139,6 @@ bool NotifySettings::change(
|
||||
if (_value) {
|
||||
return _value->change(mute, silent, muteForSeconds);
|
||||
}
|
||||
const auto asEmpty = [&] {
|
||||
if (mute == MuteChange::Mute) {
|
||||
return false;
|
||||
}
|
||||
if (silent == SilentPostsChange::Silent) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
if (asEmpty) {
|
||||
return change(MTP_peerNotifySettingsEmpty());
|
||||
}
|
||||
const auto flags = MTPDpeerNotifySettings::Flag::f_show_previews
|
||||
| ((silent == SilentPostsChange::Silent)
|
||||
? MTPDpeerNotifySettings::Flag::f_silent
|
||||
|
||||
@@ -435,7 +435,11 @@ void UserData::setName(const QString &newFirstName, const QString &newLastName,
|
||||
}
|
||||
|
||||
void UserData::setPhone(const QString &newPhone) {
|
||||
_phone = newPhone;
|
||||
if (_phone != newPhone) {
|
||||
_phone = newPhone;
|
||||
if (bareId() == Auth().userId()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UserData::setBotInfoVersion(int version) {
|
||||
|
||||
@@ -21,7 +21,7 @@ int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) {
|
||||
if (-online > now) {
|
||||
return (-online - now);
|
||||
}
|
||||
return std::numeric_limits<int32>::max();
|
||||
return std::numeric_limits<TimeId>::max();
|
||||
}
|
||||
if (online > now) {
|
||||
return online - now;
|
||||
@@ -36,7 +36,7 @@ int OnlinePhraseChangeInSeconds(TimeId online, TimeId now) {
|
||||
}
|
||||
const auto nowFull = ParseDateTime(now);
|
||||
const auto tomorrow = QDateTime(nowFull.date().addDays(1));
|
||||
return static_cast<int32>(nowFull.secsTo(tomorrow));
|
||||
return std::max(static_cast<TimeId>(nowFull.secsTo(tomorrow)), 0);
|
||||
}
|
||||
|
||||
base::optional<QString> OnlineTextSpecial(not_null<UserData*> user) {
|
||||
|
||||
@@ -34,7 +34,7 @@ inline auto SingleFlagValue(
|
||||
return FlagsValueWithMask(
|
||||
std::move(value),
|
||||
flag
|
||||
) | rpl::map([flag](typename ChangeType::Type value) {
|
||||
) | rpl::map([](typename ChangeType::Type value) {
|
||||
return !!value;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -256,6 +256,16 @@ rpl::producer<not_null<HistoryItem*>> Session::itemViewRefreshRequest() const {
|
||||
return _itemViewRefreshRequest.events();
|
||||
}
|
||||
|
||||
void Session::requestItemTextRefresh(not_null<HistoryItem*> item) {
|
||||
if (const auto i = _views.find(item); i != _views.end()) {
|
||||
for (const auto view : i->second) {
|
||||
if (const auto media = view->media()) {
|
||||
media->parentTextUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::requestAnimationPlayInline(not_null<HistoryItem*> item) {
|
||||
_animationPlayInlineRequest.fire_copy(item);
|
||||
}
|
||||
@@ -273,6 +283,14 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemRemoved() const {
|
||||
return _itemRemoved.events();
|
||||
}
|
||||
|
||||
void Session::notifyViewRemoved(not_null<const ViewElement*> view) {
|
||||
_viewRemoved.fire_copy(view);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const ViewElement*>> Session::viewRemoved() const {
|
||||
return _viewRemoved.events();
|
||||
}
|
||||
|
||||
void Session::notifyHistoryUnloaded(not_null<const History*> history) {
|
||||
_historyUnloaded.fire_copy(history);
|
||||
}
|
||||
@@ -1240,7 +1258,7 @@ void Session::gameApplyFields(
|
||||
const QString &description,
|
||||
PhotoData *photo,
|
||||
DocumentData *document) {
|
||||
if (game->accessHash || !accessHash) {
|
||||
if (game->accessHash) {
|
||||
return;
|
||||
}
|
||||
game->accessHash = accessHash;
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
[[nodiscard]] rpl::producer<not_null<ViewElement*>> viewResizeRequest() const;
|
||||
void requestItemViewRefresh(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> itemViewRefreshRequest() const;
|
||||
void requestItemTextRefresh(not_null<HistoryItem*> item);
|
||||
void requestAnimationPlayInline(not_null<HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<HistoryItem*>> animationPlayInlineRequest() const;
|
||||
void notifyHistoryUnloaded(not_null<const History*> history);
|
||||
@@ -87,6 +88,8 @@ public:
|
||||
|
||||
void notifyItemRemoved(not_null<const HistoryItem*> item);
|
||||
[[nodiscard]] rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
|
||||
void notifyViewRemoved(not_null<const ViewElement*> view);
|
||||
[[nodiscard]] rpl::producer<not_null<const ViewElement*>> viewRemoved() const;
|
||||
void notifyHistoryCleared(not_null<const History*> history);
|
||||
[[nodiscard]] rpl::producer<not_null<const History*>> historyCleared() const;
|
||||
void notifyHistoryChangeDelayed(not_null<History*> history);
|
||||
@@ -466,8 +469,10 @@ private:
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemResizeRequest;
|
||||
rpl::event_stream<not_null<ViewElement*>> _viewResizeRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemViewRefreshRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _itemTextRefreshRequest;
|
||||
rpl::event_stream<not_null<HistoryItem*>> _animationPlayInlineRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||
rpl::event_stream<not_null<const ViewElement*>> _viewRemoved;
|
||||
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
||||
rpl::event_stream<not_null<const History*>> _historyCleared;
|
||||
base::flat_set<not_null<History*>> _historiesChanged;
|
||||
|
||||
@@ -268,7 +268,7 @@ constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
|
||||
using PreparedPhotoThumbs = QMap<char, QPixmap>;
|
||||
|
||||
// [0] == -1 -- counting, [0] == -2 -- could not count
|
||||
using VoiceWaveform = QVector<char>;
|
||||
using VoiceWaveform = QVector<signed char>;
|
||||
|
||||
enum ActionOnLoad {
|
||||
ActionOnLoadNone,
|
||||
|
||||
@@ -95,7 +95,7 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
|
||||
[this](auto item) { itemRemoved(item); },
|
||||
lifetime());
|
||||
Auth().data().itemRepaintRequest(
|
||||
) | rpl::start_with_next([this](auto item) {
|
||||
) | rpl::start_with_next([=](auto item) {
|
||||
const auto history = item->history();
|
||||
if (history->textCachedFor == item) {
|
||||
history->updateChatListEntry();
|
||||
@@ -114,7 +114,7 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
|
||||
UpdateRowSection::Default | UpdateRowSection::Filtered);
|
||||
});
|
||||
|
||||
subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &data) {
|
||||
subscribe(Window::Theme::Background(), [=](const Window::Theme::BackgroundUpdate &data) {
|
||||
if (data.paletteChanged()) {
|
||||
Dialogs::Layout::clearUnreadBadgesCache();
|
||||
}
|
||||
|
||||
@@ -20,13 +20,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "application.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "autoupdater.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "messenger.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "window/window_slide_animation.h"
|
||||
#include "window/window_connecting_widget.h"
|
||||
#include "profile/profile_channel_controllers.h"
|
||||
#include "storage/storage_media_prepare.h"
|
||||
#include "data/data_session.h"
|
||||
@@ -119,10 +120,15 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
|
||||
connect(_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int)));
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus()));
|
||||
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus()));
|
||||
Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onCheckUpdateStatus()));
|
||||
onCheckUpdateStatus();
|
||||
Core::UpdateChecker checker;
|
||||
rpl::merge(
|
||||
rpl::single(rpl::empty_value()),
|
||||
checker.isLatest(),
|
||||
checker.failed(),
|
||||
checker.ready()
|
||||
) | rpl::start_with_next([=] {
|
||||
checkUpdateStatus();
|
||||
}, lifetime());
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
||||
subscribe(Adaptive::Changed(), [this] { updateForwardBar(); });
|
||||
@@ -165,16 +171,24 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
|
||||
|
||||
updateJumpToDateVisibility(true);
|
||||
updateSearchFromVisibility(true);
|
||||
setupConnectingWidget();
|
||||
}
|
||||
|
||||
void DialogsWidget::setupConnectingWidget() {
|
||||
_connecting = Window::ConnectingWidget::CreateDefaultWidget(
|
||||
this,
|
||||
Window::AdaptiveIsOneColumn());
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
void DialogsWidget::onCheckUpdateStatus() {
|
||||
if (Sandbox::updatingState() == Application::UpdatingReady) {
|
||||
void DialogsWidget::checkUpdateStatus() {
|
||||
using Checker = Core::UpdateChecker;
|
||||
if (Checker().state() == Checker::State::Ready) {
|
||||
if (_updateTelegram) return;
|
||||
_updateTelegram.create(this);
|
||||
_updateTelegram->show();
|
||||
_updateTelegram->setClickedCallback([] {
|
||||
checkReadyUpdate();
|
||||
Core::checkReadyUpdate();
|
||||
App::restart();
|
||||
});
|
||||
} else {
|
||||
@@ -276,6 +290,7 @@ void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window:
|
||||
_jumpToDate->hide(anim::type::instant);
|
||||
_chooseFromUser->hide(anim::type::instant);
|
||||
_lockUnlock->hide();
|
||||
_connecting->setForceHidden(true);
|
||||
|
||||
int delta = st::slideShift;
|
||||
if (_showDirection == Window::SlideDirection::FromLeft) {
|
||||
@@ -301,6 +316,7 @@ void DialogsWidget::animationCallback() {
|
||||
_mainMenuToggle->show();
|
||||
if (_forwardCancel) _forwardCancel->show();
|
||||
_filter->show();
|
||||
_connecting->setForceHidden(false);
|
||||
updateLockUnlockVisibility();
|
||||
updateJumpToDateVisibility(true);
|
||||
updateSearchFromVisibility(true);
|
||||
|
||||