Merge branch 'profile' into drafts
Conflicts: Telegram/SourceFiles/app.cpp Telegram/SourceFiles/codegen/style/processor.cpp Telegram/SourceFiles/history.cpp Telegram/SourceFiles/historywidget.cpp Telegram/SourceFiles/mainwidget.cpp Telegram/SourceFiles/mainwidget.h Telegram/SourceFiles/profilewidget.cpp Telegram/SourceFiles/profilewidget.h Telegram/Telegram.vcxproj Telegram/Telegram.vcxproj.filters
|
@ -20,13 +20,13 @@ downloadLibs() {
|
||||||
info_msg "QT-Version: ${_qtver}, SRC-Dir: ${srcdir}"
|
info_msg "QT-Version: ${_qtver}, SRC-Dir: ${srcdir}"
|
||||||
|
|
||||||
echo -e "Clone Qt\n"
|
echo -e "Clone Qt\n"
|
||||||
git clone git://code.qt.io/qt/qt5.git qt5_6_0
|
git clone git://code.qt.io/qt/qt5.git qt${_qtver}
|
||||||
cd qt5_6_0
|
cd qt${_qtver}
|
||||||
git checkout 5.6
|
git checkout $(echo ${_qtver} | sed -e "s/\..$//")
|
||||||
perl init-repository --module-subset=qtbase,qtimageformats
|
perl init-repository --module-subset=qtbase,qtimageformats
|
||||||
git checkout v5.6.0
|
git checkout v${_qtver}
|
||||||
cd qtbase && git checkout v5.6.0 && cd ..
|
cd qtbase && git checkout v${_qtver} && cd ..
|
||||||
cd qtimageformats && git checkout v5.6.0 && cd ..
|
cd qtimageformats && git checkout v${_qtver} && cd ..
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
echo -e "Clone Breakpad\n"
|
echo -e "Clone Breakpad\n"
|
||||||
|
@ -48,9 +48,9 @@ prepare() {
|
||||||
|
|
||||||
mkdir -p "$srcdir/Libraries"
|
mkdir -p "$srcdir/Libraries"
|
||||||
|
|
||||||
ln -s "$srcdir/qt5_6_0" "$srcdir/Libraries/qt5_6_0"
|
ln -s "$srcdir/qt${_qtver}" "$srcdir/Libraries/qt${_qtver}"
|
||||||
cd "$srcdir/Libraries/qt5_6_0/qtbase"
|
cd "$srcdir/Libraries/qt${_qtver}/qtbase"
|
||||||
git apply "$srcdir/tdesktop/Telegram/Patches/qtbase_5_6_0.diff"
|
git apply "$srcdir/tdesktop/Telegram/Patches/qtbase_$(echo ${_qtver} | sed -e "s/\./_/g").diff"
|
||||||
|
|
||||||
if [ ! -h "$srcdir/Libraries/breakpad" ]; then
|
if [ ! -h "$srcdir/Libraries/breakpad" ]; then
|
||||||
ln -s "$srcdir/breakpad" "$srcdir/Libraries/breakpad"
|
ln -s "$srcdir/breakpad" "$srcdir/Libraries/breakpad"
|
||||||
|
@ -60,7 +60,6 @@ prepare() {
|
||||||
sed -i 's/CUSTOM_API_ID//g' "$srcdir/tdesktop/Telegram/Telegram.pro"
|
sed -i 's/CUSTOM_API_ID//g' "$srcdir/tdesktop/Telegram/Telegram.pro"
|
||||||
sed -i 's,LIBS += /usr/local/lib/libxkbcommon.a,,g' "$srcdir/tdesktop/Telegram/Telegram.pro"
|
sed -i 's,LIBS += /usr/local/lib/libxkbcommon.a,,g' "$srcdir/tdesktop/Telegram/Telegram.pro"
|
||||||
sed -i 's,LIBS += /usr/local/lib/libz.a,LIBS += -lz,g' "$srcdir/tdesktop/Telegram/Telegram.pro"
|
sed -i 's,LIBS += /usr/local/lib/libz.a,LIBS += -lz,g' "$srcdir/tdesktop/Telegram/Telegram.pro"
|
||||||
sed -i "s,/usr/local/tdesktop/Qt-5.6.0,$srcdir/qt,g" "$srcdir/tdesktop/Telegram/Telegram.pro"
|
|
||||||
|
|
||||||
local options=""
|
local options=""
|
||||||
|
|
||||||
|
@ -98,7 +97,7 @@ build() {
|
||||||
|
|
||||||
info_msg "Build patched Qt"
|
info_msg "Build patched Qt"
|
||||||
# Build patched Qt
|
# Build patched Qt
|
||||||
cd "$srcdir/Libraries/qt5_6_0"
|
cd "$srcdir/Libraries/qt${_qtver}"
|
||||||
./configure -prefix "$srcdir/qt" -release -opensource -confirm-license -qt-zlib \
|
./configure -prefix "$srcdir/qt" -release -opensource -confirm-license -qt-zlib \
|
||||||
-qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb \
|
-qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb \
|
||||||
-qt-xkbcommon-x11 -no-opengl -static -nomake examples -nomake tests
|
-qt-xkbcommon-x11 -no-opengl -static -nomake examples -nomake tests
|
||||||
|
@ -117,21 +116,21 @@ build() {
|
||||||
# Build codegen_style
|
# Build codegen_style
|
||||||
mkdir -p "$srcdir/tdesktop/Linux/obj/codegen_style/Debug"
|
mkdir -p "$srcdir/tdesktop/Linux/obj/codegen_style/Debug"
|
||||||
cd "$srcdir/tdesktop/Linux/obj/codegen_style/Debug"
|
cd "$srcdir/tdesktop/Linux/obj/codegen_style/Debug"
|
||||||
qmake CONFIG+=debug "../../../../Telegram/build/qmake/codegen_style/codegen_style.pro"
|
qmake QT_TDESKTOP_PATH=${srcdir}/qt QT_TDESKTOP_VERSION=${_qtver} CONFIG+=debug "../../../../Telegram/build/qmake/codegen_style/codegen_style.pro"
|
||||||
make --silent -j4
|
make --silent -j4
|
||||||
|
|
||||||
info_msg "Build codegen_numbers"
|
info_msg "Build codegen_numbers"
|
||||||
# Build codegen_numbers
|
# Build codegen_numbers
|
||||||
mkdir -p "$srcdir/tdesktop/Linux/obj/codegen_numbers/Debug"
|
mkdir -p "$srcdir/tdesktop/Linux/obj/codegen_numbers/Debug"
|
||||||
cd "$srcdir/tdesktop/Linux/obj/codegen_numbers/Debug"
|
cd "$srcdir/tdesktop/Linux/obj/codegen_numbers/Debug"
|
||||||
qmake CONFIG+=debug "../../../../Telegram/build/qmake/codegen_numbers/codegen_numbers.pro"
|
qmake QT_TDESKTOP_PATH=${srcdir}/qt QT_TDESKTOP_VERSION=${_qtver} CONFIG+=debug "../../../../Telegram/build/qmake/codegen_numbers/codegen_numbers.pro"
|
||||||
make --silent -j4
|
make --silent -j4
|
||||||
|
|
||||||
info_msg "Build MetaLang"
|
info_msg "Build MetaLang"
|
||||||
# Build MetaLang
|
# Build MetaLang
|
||||||
mkdir -p "$srcdir/tdesktop/Linux/DebugIntermediateLang"
|
mkdir -p "$srcdir/tdesktop/Linux/DebugIntermediateLang"
|
||||||
cd "$srcdir/tdesktop/Linux/DebugIntermediateLang"
|
cd "$srcdir/tdesktop/Linux/DebugIntermediateLang"
|
||||||
qmake CONFIG+=debug "../../Telegram/MetaLang.pro"
|
qmake QT_TDESKTOP_PATH=${srcdir}/qt QT_TDESKTOP_VERSION=${_qtver} CONFIG+=debug "../../Telegram/MetaLang.pro"
|
||||||
make --silent -j4
|
make --silent -j4
|
||||||
|
|
||||||
info_msg "Build Telegram Desktop"
|
info_msg "Build Telegram Desktop"
|
||||||
|
@ -142,7 +141,7 @@ build() {
|
||||||
./../codegen/Debug/codegen_style "-I./../../Telegram/Resources" "-I./../../Telegram/SourceFiles" "-o./GeneratedFiles/styles" all_files.style --rebuild
|
./../codegen/Debug/codegen_style "-I./../../Telegram/Resources" "-I./../../Telegram/SourceFiles" "-o./GeneratedFiles/styles" all_files.style --rebuild
|
||||||
./../codegen/Debug/codegen_numbers "-o./GeneratedFiles" "./../../Telegram/Resources/numbers.txt"
|
./../codegen/Debug/codegen_numbers "-o./GeneratedFiles" "./../../Telegram/Resources/numbers.txt"
|
||||||
./../DebugLang/MetaLang -lang_in ./../../Telegram/Resources/langs/lang.strings -lang_out ./GeneratedFiles/lang_auto
|
./../DebugLang/MetaLang -lang_in ./../../Telegram/Resources/langs/lang.strings -lang_out ./GeneratedFiles/lang_auto
|
||||||
qmake CONFIG+=debug "../../Telegram/Telegram.pro"
|
qmake QT_TDESKTOP_PATH=${srcdir}/qt QT_TDESKTOP_VERSION=${_qtver} CONFIG+=debug "../../Telegram/Telegram.pro"
|
||||||
make -j4
|
make -j4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ Cya='\e[0;36m'; BCya='\e[1;36m'; UCya='\e[4;36m'; ICya='\e[0;96m';
|
||||||
Whi='\e[0;37m'; BWhi='\e[1;37m'; UWhi='\e[4;37m'; IWhi='\e[0;97m'; BIWhi='\e[1;97m'; On_Whi='\e[47m'; On_IWhi='\e[0;107m';
|
Whi='\e[0;37m'; BWhi='\e[1;37m'; UWhi='\e[4;37m'; IWhi='\e[0;97m'; BIWhi='\e[1;97m'; On_Whi='\e[47m'; On_IWhi='\e[0;107m';
|
||||||
|
|
||||||
# Set variables
|
# Set variables
|
||||||
_qtver=5.5.1
|
_qtver=5.6.0
|
||||||
srcdir=${PWD}
|
srcdir=${PWD}
|
||||||
|
|
||||||
start_msg() {
|
start_msg() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
|
||||||
* Windows XP - Windows 10 (**not** RT)
|
* Windows XP - Windows 10 (**not** RT)
|
||||||
* Mac OS X 10.8 - Mac OS X 10.11
|
* Mac OS X 10.8 - Mac OS X 10.11
|
||||||
* Mac OS X 10.6 - Mac OS X 10.7 (separate build)
|
* Mac OS X 10.6 - Mac OS X 10.7 (separate build)
|
||||||
* Ubuntu 12.04 - Ubuntu 15.04
|
* Ubuntu 12.04 - Ubuntu 16.04
|
||||||
* Fedora 22
|
* Fedora 22
|
||||||
|
|
||||||
## Third-party libraries
|
## Third-party libraries
|
||||||
|
|
|
@ -27,7 +27,4 @@ HEADERS += \
|
||||||
./SourceFiles/_other/memain.h \
|
./SourceFiles/_other/memain.h \
|
||||||
./SourceFiles/_other/genemoji.h \
|
./SourceFiles/_other/genemoji.h \
|
||||||
|
|
||||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\
|
include(qt_static.pri)
|
||||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.5.1/QtCore\
|
|
||||||
./../../Libraries/QtStatic/qtbase/include\
|
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,4 @@ HEADERS += \
|
||||||
./SourceFiles/_other/mlmain.h \
|
./SourceFiles/_other/mlmain.h \
|
||||||
./SourceFiles/_other/genlang.h \
|
./SourceFiles/_other/genlang.h \
|
||||||
|
|
||||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\
|
include(qt_static.pri)
|
||||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.5.1/QtCore\
|
|
||||||
./../../Libraries/QtStatic/qtbase/include\
|
|
||||||
|
|
||||||
|
|
|
@ -35,8 +35,6 @@ unix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\
|
include(qt_static.pri)
|
||||||
./../../Libraries/QtStatic/qtbase/include/QtCore/5.5.1/QtCore\
|
|
||||||
./../../Libraries/QtStatic/qtbase/include
|
|
||||||
|
|
||||||
LIBS += -lcrypto -lssl -lz -llzma
|
LIBS += -lcrypto -lssl -lz -llzma
|
||||||
|
|
|
@ -24,3 +24,4 @@ using "basic_types.style";
|
||||||
using "basic.style";
|
using "basic.style";
|
||||||
|
|
||||||
using "overview/overview.style";
|
using "overview/overview.style";
|
||||||
|
using "profile/profile.style";
|
||||||
|
|
|
@ -55,10 +55,15 @@ wndMinWidth: 380px;
|
||||||
adaptiveNormalWidth: 640px;
|
adaptiveNormalWidth: 640px;
|
||||||
adaptiveWideWidth: 1366px;
|
adaptiveWideWidth: 1366px;
|
||||||
|
|
||||||
|
windowBg: #fff; // fallback for background: white
|
||||||
|
windowTextFg: #000; // fallback for text color: black
|
||||||
|
windowSubTextFg: #8a8a8a; // fallback for subtext color: gray
|
||||||
|
windowActiveTextFg: #1485c2; // fallback for active color: blue online
|
||||||
|
windowShadowFg: #000; // fallback for shadow color
|
||||||
|
|
||||||
wndMinHeight: 480px;
|
wndMinHeight: 480px;
|
||||||
wndDefWidth: 800px;
|
wndDefWidth: 800px;
|
||||||
wndDefHeight: 600px;
|
wndDefHeight: 600px;
|
||||||
wndBG: white;
|
|
||||||
wndShadow: sprite(209px, 46px, 19px, 19px);
|
wndShadow: sprite(209px, 46px, 19px, 19px);
|
||||||
wndShadowShift: 1px;
|
wndShadowShift: 1px;
|
||||||
|
|
||||||
|
@ -69,9 +74,10 @@ overBg: #edf2f5;
|
||||||
|
|
||||||
labelDefFlat: flatLabel {
|
labelDefFlat: flatLabel {
|
||||||
font: font(fsize);
|
font: font(fsize);
|
||||||
minWidth: 100px;
|
|
||||||
width: 0px;
|
width: 0px;
|
||||||
|
maxHeight: 0px;
|
||||||
align: align(left);
|
align: align(left);
|
||||||
|
textFg: windowTextFg;
|
||||||
}
|
}
|
||||||
|
|
||||||
boxBg: white;
|
boxBg: white;
|
||||||
|
@ -140,6 +146,29 @@ boxLabel: flatLabel(labelDefFlat) {
|
||||||
align: align(topleft);
|
align: align(topleft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultLeftOutlineButton: OutlineButton {
|
||||||
|
outlineWidth: 3px;
|
||||||
|
outlineFg: windowBg;
|
||||||
|
outlineFgOver: #3fb0e4;
|
||||||
|
|
||||||
|
textBg: windowBg;
|
||||||
|
textBgOver: #f2f7fa;
|
||||||
|
|
||||||
|
textFg: windowActiveTextFg;
|
||||||
|
textFgOver: windowActiveTextFg;
|
||||||
|
|
||||||
|
font: normalFont;
|
||||||
|
padding: margins(11px, 6px, 11px, 6px);
|
||||||
|
}
|
||||||
|
attentionLeftOutlineButton: OutlineButton(defaultLeftOutlineButton) {
|
||||||
|
outlineFgOver: #e43f3f;
|
||||||
|
|
||||||
|
textBgOver: #faf2f2;
|
||||||
|
|
||||||
|
textFg: #d15948;
|
||||||
|
textFgOver: #d15948;
|
||||||
|
}
|
||||||
|
|
||||||
defaultInputArea: InputArea {
|
defaultInputArea: InputArea {
|
||||||
textFg: black;
|
textFg: black;
|
||||||
textMargins: margins(5px, 6px, 5px, 4px);
|
textMargins: margins(5px, 6px, 5px, 4px);
|
||||||
|
@ -370,9 +399,10 @@ statusFont: font(fsize);
|
||||||
versionColor: #777;
|
versionColor: #777;
|
||||||
|
|
||||||
shadowColor: rgba(0, 0, 0, 24);
|
shadowColor: rgba(0, 0, 0, 24);
|
||||||
|
shadowToggleDuration: 200;
|
||||||
|
|
||||||
slideDuration: 240;
|
slideDuration: 240;
|
||||||
slideShift: 0.3;
|
slideShift: 100px;
|
||||||
slideFadeOut: 0.3;
|
slideFadeOut: 0.3;
|
||||||
slideShadow: sprite(348px, 71px, 48px, 1px);
|
slideShadow: sprite(348px, 71px, 48px, 1px);
|
||||||
slideFunction: transition(easeOutCirc);
|
slideFunction: transition(easeOutCirc);
|
||||||
|
@ -1657,18 +1687,18 @@ confirmCompressedSkip: 10px;
|
||||||
|
|
||||||
profileMaxWidth: 410px;
|
profileMaxWidth: 410px;
|
||||||
profilePadding: margins(28px, 30px, 28px, 0px);
|
profilePadding: margins(28px, 30px, 28px, 0px);
|
||||||
profilePhotoSize: 120px;
|
//profilePhotoSize: 120px;
|
||||||
profileNameLeft: 21px;
|
//profileNameLeft: 21px;
|
||||||
profileNameTop: -1px;
|
//profileNameTop: -1px;
|
||||||
profileNameFont: font(20px);
|
//profileNameFont: font(20px);
|
||||||
profileStatusLeft: 22px;
|
//profileStatusLeft: 22px;
|
||||||
profileStatusTop: 31px;
|
//profileStatusTop: 31px;
|
||||||
profileStatusFont: font(fsize);
|
//profileStatusFont: font(fsize);
|
||||||
profilePhoneLeft: 22px;
|
profilePhoneLeft: 22px;
|
||||||
profilePhoneTop: 62px;
|
profilePhoneTop: 62px;
|
||||||
profilePhoneFont: font(16px);
|
profilePhoneFont: font(16px);
|
||||||
profileButtonTop: 18px;
|
//profileButtonTop: 18px;
|
||||||
profileButtonSkip: 10px;
|
//profileButtonSkip: 10px;
|
||||||
profileHeaderFont: font(20px);
|
profileHeaderFont: font(20px);
|
||||||
profileHeaderColor: black;
|
profileHeaderColor: black;
|
||||||
profileHeaderSkip: 59px;
|
profileHeaderSkip: 59px;
|
||||||
|
@ -2444,7 +2474,7 @@ inlineResultsSkip: 3px;
|
||||||
inlineMediaHeight: 96px;
|
inlineMediaHeight: 96px;
|
||||||
inlineThumbSize: 64px;
|
inlineThumbSize: 64px;
|
||||||
inlineThumbSkip: 10px;
|
inlineThumbSkip: 10px;
|
||||||
inlineDescriptionFg: #8a8a8a;
|
inlineDescriptionFg: windowSubTextFg;
|
||||||
inlineRowMargin: 6px;
|
inlineRowMargin: 6px;
|
||||||
inlineRowBorder: 1px;
|
inlineRowBorder: 1px;
|
||||||
inlineRowBorderFg: #eaeaea;
|
inlineRowBorderFg: #eaeaea;
|
||||||
|
|
|
@ -210,9 +210,11 @@ slider {
|
||||||
|
|
||||||
flatLabel {
|
flatLabel {
|
||||||
font: font;
|
font: font;
|
||||||
minWidth: pixels;
|
margin: margins;
|
||||||
width: pixels;
|
width: pixels;
|
||||||
align: align;
|
align: align;
|
||||||
|
textFg: color;
|
||||||
|
maxHeight: pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
switcher {
|
switcher {
|
||||||
|
@ -306,6 +308,8 @@ BoxButton {
|
||||||
|
|
||||||
textTop: pixels;
|
textTop: pixels;
|
||||||
|
|
||||||
|
icon: icon;
|
||||||
|
|
||||||
font: font;
|
font: font;
|
||||||
duration: int;
|
duration: int;
|
||||||
}
|
}
|
||||||
|
@ -411,3 +415,18 @@ PeerAvatarButton {
|
||||||
size: pixels;
|
size: pixels;
|
||||||
photoSize: pixels;
|
photoSize: pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OutlineButton {
|
||||||
|
outlineWidth: pixels;
|
||||||
|
outlineFg: color;
|
||||||
|
outlineFgOver: color;
|
||||||
|
|
||||||
|
textBg: color;
|
||||||
|
textBgOver: color;
|
||||||
|
|
||||||
|
textFg: color;
|
||||||
|
textFgOver: color;
|
||||||
|
|
||||||
|
font: font;
|
||||||
|
padding: margins;
|
||||||
|
}
|
||||||
|
|
BIN
Telegram/Resources/icons/profile_add_member.png
Normal file
After Width: | Height: | Size: 319 B |
BIN
Telegram/Resources/icons/profile_add_member@2x.png
Normal file
After Width: | Height: | Size: 592 B |
BIN
Telegram/Resources/icons/profile_admin_star.png
Normal file
After Width: | Height: | Size: 313 B |
BIN
Telegram/Resources/icons/profile_admin_star@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/profile_divider_fill.png
Normal file
After Width: | Height: | Size: 96 B |
BIN
Telegram/Resources/icons/profile_divider_fill@2x.png
Normal file
After Width: | Height: | Size: 109 B |
BIN
Telegram/Resources/icons/profile_divider_left.png
Normal file
After Width: | Height: | Size: 114 B |
BIN
Telegram/Resources/icons/profile_divider_left@2x.png
Normal file
After Width: | Height: | Size: 155 B |
BIN
Telegram/Resources/icons/topbar_back_arrow.png
Normal file
After Width: | Height: | Size: 137 B |
BIN
Telegram/Resources/icons/topbar_back_arrow@2x.png
Normal file
After Width: | Height: | Size: 252 B |
|
@ -117,8 +117,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_channel_status" = "channel";
|
"lng_channel_status" = "channel";
|
||||||
"lng_group_status" = "group";
|
"lng_group_status" = "group";
|
||||||
|
|
||||||
"lng_channel_members_link" = "{count:_not_used_|# member|# members} »";
|
"lng_channel_members_link" = "{count:_not_used_|# member|# members}";
|
||||||
"lng_channel_admins_link" = "{count:Manage administrators|# administrator|# administrators} »";
|
"lng_channel_admins_link" = "{count:_not_used_|# administrator|# administrators}";
|
||||||
|
|
||||||
"lng_server_error" = "Internal server error.";
|
"lng_server_error" = "Internal server error.";
|
||||||
"lng_flood_error" = "Too many tries. Please try again later.";
|
"lng_flood_error" = "Too many tries. Please try again later.";
|
||||||
|
@ -405,12 +405,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_profile_actions_section" = "Actions";
|
"lng_profile_actions_section" = "Actions";
|
||||||
"lng_profile_bot_settings" = "Settings";
|
"lng_profile_bot_settings" = "Settings";
|
||||||
"lng_profile_bot_help" = "Help";
|
"lng_profile_bot_help" = "Help";
|
||||||
|
"lng_profile_invite_link_section" = "Invite link";
|
||||||
"lng_profile_create_public_link" = "Create public link";
|
"lng_profile_create_public_link" = "Create public link";
|
||||||
"lng_profile_edit_public_link" = "Edit public link";
|
"lng_profile_edit_public_link" = "Edit public link";
|
||||||
|
"lng_profile_manage_admins" = "Manage administrators";
|
||||||
"lng_profile_participants_section" = "Members";
|
"lng_profile_participants_section" = "Members";
|
||||||
"lng_profile_info" = "Contact info";
|
"lng_profile_info_section" = "Info";
|
||||||
"lng_profile_group_info" = "Group info";
|
"lng_profile_mobile_number" = "Mobile:";
|
||||||
"lng_profile_channel_info" = "Channel info";
|
"lng_profile_username" = "Username:";
|
||||||
|
"lng_profile_link" = "Link:";
|
||||||
"lng_profile_add_contact" = "Add Contact";
|
"lng_profile_add_contact" = "Add Contact";
|
||||||
"lng_profile_edit_contact" = "Edit";
|
"lng_profile_edit_contact" = "Edit";
|
||||||
"lng_profile_enable_notifications" = "Notifications";
|
"lng_profile_enable_notifications" = "Notifications";
|
||||||
|
@ -421,6 +424,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_profile_delete_channel" = "Delete channel";
|
"lng_profile_delete_channel" = "Delete channel";
|
||||||
"lng_profile_leave_group" = "Leave group";
|
"lng_profile_leave_group" = "Leave group";
|
||||||
"lng_profile_delete_group" = "Delete group";
|
"lng_profile_delete_group" = "Delete group";
|
||||||
|
"lng_profile_report" = "Report";
|
||||||
"lng_profile_search_messages" = "Search for messages";
|
"lng_profile_search_messages" = "Search for messages";
|
||||||
"lng_profile_block_user" = "Block user";
|
"lng_profile_block_user" = "Block user";
|
||||||
"lng_profile_unblock_user" = "Unblock user";
|
"lng_profile_unblock_user" = "Unblock user";
|
||||||
|
@ -432,6 +436,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_profile_delete_contact" = "Delete";
|
"lng_profile_delete_contact" = "Delete";
|
||||||
"lng_profile_set_group_photo" = "Set Photo";
|
"lng_profile_set_group_photo" = "Set Photo";
|
||||||
"lng_profile_add_participant" = "Add Members";
|
"lng_profile_add_participant" = "Add Members";
|
||||||
|
"lng_profile_view_channel" = "View Channel";
|
||||||
|
"lng_profile_join_channel" = "Join";
|
||||||
"lng_profile_delete_and_exit" = "Leave";
|
"lng_profile_delete_and_exit" = "Leave";
|
||||||
"lng_profile_kick" = "Remove";
|
"lng_profile_kick" = "Remove";
|
||||||
"lng_profile_admin" = "admin";
|
"lng_profile_admin" = "admin";
|
||||||
|
@ -441,20 +447,33 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_profile_loading" = "Loading...";
|
"lng_profile_loading" = "Loading...";
|
||||||
"lng_profile_shared_media" = "Shared media";
|
"lng_profile_shared_media" = "Shared media";
|
||||||
"lng_profile_no_media" = "No media in this conversation.";
|
"lng_profile_no_media" = "No media in this conversation.";
|
||||||
"lng_profile_photos" = "{count:_not_used_|# photo|# photos} »";
|
"lng_profile_photos" = "{count:_not_used_|# photo|# photos}";
|
||||||
"lng_profile_photos_header" = "Photos overview";
|
"lng_profile_photos_header" = "Photos overview";
|
||||||
"lng_profile_videos" = "{count:_not_used_|# video file|# video files} »";
|
"lng_profile_videos" = "{count:_not_used_|# video file|# video files}";
|
||||||
"lng_profile_videos_header" = "Video files overview";
|
"lng_profile_videos_header" = "Video files overview";
|
||||||
"lng_profile_songs" = "{count:_not_used_|# audio file|# audio files} »";
|
"lng_profile_songs" = "{count:_not_used_|# audio file|# audio files}";
|
||||||
"lng_profile_songs_header" = "Audio files overview";
|
"lng_profile_songs_header" = "Audio files overview";
|
||||||
"lng_profile_files" = "{count:_not_used_|# file|# files} »";
|
"lng_profile_files" = "{count:_not_used_|# file|# files}";
|
||||||
"lng_profile_files_header" = "Files overview";
|
"lng_profile_files_header" = "Files overview";
|
||||||
"lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages} »";
|
"lng_profile_audios" = "{count:_not_used_|# voice message|# voice messages}";
|
||||||
"lng_profile_audios_header" = "Voice messages overview";
|
"lng_profile_audios_header" = "Voice messages overview";
|
||||||
"lng_profile_shared_links" = "{count:_not_used_|# shared link|# shared links} »";
|
"lng_profile_shared_links" = "{count:_not_used_|# shared link|# shared links}";
|
||||||
"lng_profile_shared_links_header" = "Shared links overview";
|
"lng_profile_shared_links_header" = "Shared links overview";
|
||||||
"lng_profile_copy_phone" = "Copy phone number";
|
"lng_profile_copy_phone" = "Copy phone number";
|
||||||
"lng_profile_copy_fullname" = "Copy name";
|
"lng_profile_copy_fullname" = "Copy name";
|
||||||
|
"lng_profile_drop_area_title" = "Drop your image here";
|
||||||
|
"lng_profile_drop_area_subtitle" = "to set it as a group photo";
|
||||||
|
"lng_profile_drop_area_subtitle_channel" = "to set it as a channel photo";
|
||||||
|
"lng_profile_top_bar_share_contact" = "Share";
|
||||||
|
|
||||||
|
"lng_report_title" = "Report channel";
|
||||||
|
"lng_report_reason_spam" = "Spam";
|
||||||
|
"lng_report_reason_violence" = "Violence";
|
||||||
|
"lng_report_reason_pornography" = "Pornography";
|
||||||
|
"lng_report_reason_other" = "Other";
|
||||||
|
"lng_report_reason_description" = "Description";
|
||||||
|
"lng_report_button" = "Report";
|
||||||
|
"lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon.";
|
||||||
|
|
||||||
"lng_channel_add_admins" = "New administrator";
|
"lng_channel_add_admins" = "New administrator";
|
||||||
"lng_channel_add_members" = "Add members";
|
"lng_channel_add_members" = "Add members";
|
||||||
|
@ -550,6 +569,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_action_pinned_media_sticker" = "a sticker";
|
"lng_action_pinned_media_sticker" = "a sticker";
|
||||||
|
|
||||||
"lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached";
|
"lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached";
|
||||||
|
"lng_profile_migrate_body" = "To get over this limit, you can upgrade your group to a supergroup.";
|
||||||
|
"lng_profile_migrate_learn_more" = "Learn more »";
|
||||||
"lng_profile_migrate_about" = "If you'd like to go over this limit, you can upgrade your group to a supergroup. In supergroups:";
|
"lng_profile_migrate_about" = "If you'd like to go over this limit, you can upgrade your group to a supergroup. In supergroups:";
|
||||||
"lng_profile_migrate_feature1" = "— The members limit is {count:_not_used_|# user|# users}";
|
"lng_profile_migrate_feature1" = "— The members limit is {count:_not_used_|# user|# users}";
|
||||||
"lng_profile_migrate_feature2" = "— New members see the entire chat history";
|
"lng_profile_migrate_feature2" = "— New members see the entire chat history";
|
||||||
|
@ -583,7 +604,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
"lng_group_invite_create" = "Create an invite link";
|
"lng_group_invite_create" = "Create an invite link";
|
||||||
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
||||||
"lng_group_invite_create_new" = "Revoke invite link";
|
"lng_group_invite_create_new" = "Revoke invite link";
|
||||||
"lng_group_invite_about_new" = "Your previous link will be deactivated\nand we'll generate a new invite link for you.";
|
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
|
||||||
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
||||||
"lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already.";
|
"lng_group_invite_no_room" = "Unable to join this group because there are too many members in it already.";
|
||||||
|
|
||||||
|
@ -902,6 +923,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
// Not used
|
// Not used
|
||||||
|
|
||||||
"lng_topbar_info" = "Info";
|
"lng_topbar_info" = "Info";
|
||||||
|
"lng_profile_group_info" = "Group info";
|
||||||
|
"lng_profile_channel_info" = "Channel info";
|
||||||
|
|
||||||
// Wnd specific
|
// Wnd specific
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 0,9,49,0
|
FILEVERSION 0,9,49,1
|
||||||
PRODUCTVERSION 0,9,49,0
|
PRODUCTVERSION 0,9,49,1
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -51,10 +51,10 @@ BEGIN
|
||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||||
VALUE "FileVersion", "0.9.49.0"
|
VALUE "FileVersion", "0.9.49.1"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
|
||||||
VALUE "ProductName", "Telegram Desktop"
|
VALUE "ProductName", "Telegram Desktop"
|
||||||
VALUE "ProductVersion", "0.9.49.0"
|
VALUE "ProductVersion", "0.9.49.1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 0,9,49,0
|
FILEVERSION 0,9,49,1
|
||||||
PRODUCTVERSION 0,9,49,0
|
PRODUCTVERSION 0,9,49,1
|
||||||
FILEFLAGSMASK 0x3fL
|
FILEFLAGSMASK 0x3fL
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
FILEFLAGS 0x1L
|
FILEFLAGS 0x1L
|
||||||
|
@ -43,10 +43,10 @@ BEGIN
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||||
VALUE "FileDescription", "Telegram Updater"
|
VALUE "FileDescription", "Telegram Updater"
|
||||||
VALUE "FileVersion", "0.9.49.0"
|
VALUE "FileVersion", "0.9.49.1"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
|
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
|
||||||
VALUE "ProductName", "Telegram Desktop"
|
VALUE "ProductName", "Telegram Desktop"
|
||||||
VALUE "ProductVersion", "0.9.49.0"
|
VALUE "ProductVersion", "0.9.49.1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
#include "observer_peer.h"
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -96,6 +97,10 @@ void ApiWrap::resolveMessageDatas() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::updatesReceived(const MTPUpdates &updates) {
|
||||||
|
App::main()->sentUpdatesReceived(updates);
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
|
void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
|
||||||
switch (msgs.type()) {
|
switch (msgs.type()) {
|
||||||
case mtpc_messages_messages: {
|
case mtpc_messages_messages: {
|
||||||
|
@ -174,8 +179,8 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
|
||||||
badVersion = (!vc.isEmpty() && vc.at(0).type() == mtpc_channel && vc.at(0).c_channel().vversion.v < peer->asChannel()->version);
|
badVersion = (!vc.isEmpty() && vc.at(0).type() == mtpc_channel && vc.at(0).c_channel().vversion.v < peer->asChannel()->version);
|
||||||
}
|
}
|
||||||
|
|
||||||
App::feedUsers(d.vusers, false);
|
App::feedUsers(d.vusers);
|
||||||
App::feedChats(d.vchats, false);
|
App::feedChats(d.vchats);
|
||||||
|
|
||||||
if (peer->isChat()) {
|
if (peer->isChat()) {
|
||||||
if (d.vfull_chat.type() != mtpc_chatFull) {
|
if (d.vfull_chat.type() != mtpc_chatFull) {
|
||||||
|
@ -206,9 +211,9 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
|
||||||
} else {
|
} else {
|
||||||
chat->photoId = 0;
|
chat->photoId = 0;
|
||||||
}
|
}
|
||||||
chat->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString();
|
chat->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
|
||||||
|
|
||||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
||||||
} else if (peer->isChannel()) {
|
} else if (peer->isChannel()) {
|
||||||
if (d.vfull_chat.type() != mtpc_channelFull) {
|
if (d.vfull_chat.type() != mtpc_channelFull) {
|
||||||
LOG(("MTP Error: bad type in gotChatFull for channel: %1").arg(d.vfull_chat.type()));
|
LOG(("MTP Error: bad type in gotChatFull for channel: %1").arg(d.vfull_chat.type()));
|
||||||
|
@ -217,6 +222,10 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
|
||||||
auto &f(d.vfull_chat.c_channelFull());
|
auto &f(d.vfull_chat.c_channelFull());
|
||||||
PhotoData *photo = App::feedPhoto(f.vchat_photo);
|
PhotoData *photo = App::feedPhoto(f.vchat_photo);
|
||||||
ChannelData *channel = peer->asChannel();
|
ChannelData *channel = peer->asChannel();
|
||||||
|
|
||||||
|
auto canViewAdmins = channel->canViewAdmins();
|
||||||
|
auto canViewMembers = channel->canViewMembers();
|
||||||
|
|
||||||
channel->flagsFull = f.vflags.v;
|
channel->flagsFull = f.vflags.v;
|
||||||
if (photo) {
|
if (photo) {
|
||||||
channel->photoId = photo->id;
|
channel->photoId = photo->id;
|
||||||
|
@ -267,17 +276,10 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
channel->about = qs(f.vabout);
|
channel->setAbout(qs(f.vabout));
|
||||||
int32 newCount = f.has_participants_count() ? f.vparticipants_count.v : 0;
|
channel->setMembersCount(f.has_participants_count() ? f.vparticipants_count.v : 0);
|
||||||
if (newCount != channel->count) {
|
channel->setAdminsCount(f.has_admins_count() ? f.vadmins_count.v : 0);
|
||||||
if (channel->isMegagroup() && !channel->mgInfo->lastParticipants.isEmpty()) {
|
channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
|
||||||
channel->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
|
|
||||||
channel->mgInfo->lastParticipantsCount = channel->count;
|
|
||||||
}
|
|
||||||
channel->count = newCount;
|
|
||||||
}
|
|
||||||
channel->adminsCount = f.has_admins_count() ? f.vadmins_count.v : 0;
|
|
||||||
channel->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString();
|
|
||||||
if (History *h = App::historyLoaded(channel->id)) {
|
if (History *h = App::historyLoaded(channel->id)) {
|
||||||
if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) {
|
if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) {
|
||||||
h->setUnreadCount(f.vunread_count.v);
|
h->setUnreadCount(f.vunread_count.v);
|
||||||
|
@ -294,7 +296,10 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
|
||||||
}
|
}
|
||||||
channel->fullUpdated();
|
channel->fullUpdated();
|
||||||
|
|
||||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
if (canViewAdmins != channel->canViewAdmins()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelCanViewAdmins);
|
||||||
|
if (canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelCanViewMembers);
|
||||||
|
|
||||||
|
notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req) {
|
if (req) {
|
||||||
|
@ -313,18 +318,17 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
|
||||||
}
|
}
|
||||||
App::clearPeerUpdated(peer);
|
App::clearPeerUpdated(peer);
|
||||||
emit fullPeerUpdated(peer);
|
emit fullPeerUpdated(peer);
|
||||||
App::emitPeerUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req) {
|
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req) {
|
||||||
const auto &d(result.c_userFull());
|
const auto &d(result.c_userFull());
|
||||||
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser), false);
|
App::feedUsers(MTP_vector<MTPUser>(1, d.vuser));
|
||||||
if (d.has_profile_photo()) {
|
if (d.has_profile_photo()) {
|
||||||
App::feedPhoto(d.vprofile_photo);
|
App::feedPhoto(d.vprofile_photo);
|
||||||
}
|
}
|
||||||
App::feedUserLink(MTP_int(peerToUser(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link, false);
|
App::feedUserLink(MTP_int(peerToUser(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
|
notifySettingReceived(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.has_bot_info()) {
|
if (d.has_bot_info()) {
|
||||||
|
@ -332,8 +336,8 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI
|
||||||
} else {
|
} else {
|
||||||
peer->asUser()->setBotInfoVersion(-1);
|
peer->asUser()->setBotInfoVersion(-1);
|
||||||
}
|
}
|
||||||
peer->asUser()->blocked = d.is_blocked() ? UserIsBlocked : UserIsNotBlocked;
|
peer->asUser()->setBlockStatus(d.is_blocked() ? UserData::BlockStatus::Blocked : UserData::BlockStatus::NotBlocked);
|
||||||
peer->asUser()->about = d.has_about() ? qs(d.vabout) : QString();
|
peer->asUser()->setAbout(d.has_about() ? qs(d.vabout) : QString());
|
||||||
|
|
||||||
if (req) {
|
if (req) {
|
||||||
QMap<PeerData*, mtpRequestId>::iterator i = _fullPeerRequests.find(peer);
|
QMap<PeerData*, mtpRequestId>::iterator i = _fullPeerRequests.find(peer);
|
||||||
|
@ -343,7 +347,6 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI
|
||||||
}
|
}
|
||||||
App::clearPeerUpdated(peer);
|
App::clearPeerUpdated(peer);
|
||||||
emit fullPeerUpdated(peer);
|
emit fullPeerUpdated(peer);
|
||||||
App::emitPeerUpdated();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ApiWrap::gotPeerFullFailed(PeerData *peer, const RPCError &error) {
|
bool ApiWrap::gotPeerFullFailed(PeerData *peer, const RPCError &error) {
|
||||||
|
@ -538,14 +541,19 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP
|
||||||
h->clearLastKeyboard();
|
h->clearLastKeyboard();
|
||||||
if (App::main()) App::main()->updateBotKeyboard(h);
|
if (App::main()) App::main()->updateBotKeyboard(h);
|
||||||
}
|
}
|
||||||
if (d.vcount.v > peer->count) {
|
int newMembersCount = qMax(d.vcount.v, v.count());
|
||||||
peer->count = d.vcount.v;
|
if (newMembersCount > peer->membersCount()) {
|
||||||
} else if (v.count() > peer->count) {
|
peer->setMembersCount(newMembersCount);
|
||||||
peer->count = v.count();
|
|
||||||
}
|
}
|
||||||
if (!bots && v.isEmpty()) {
|
if (!bots) {
|
||||||
peer->count = peer->mgInfo->lastParticipants.size();
|
if (v.isEmpty()) {
|
||||||
|
peer->setMembersCount(peer->mgInfo->lastParticipants.size());
|
||||||
|
}
|
||||||
|
Notify::PeerUpdate update(peer);
|
||||||
|
update.flags |= Notify::PeerUpdate::Flag::MembersChanged | Notify::PeerUpdate::Flag::AdminsChanged;
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
|
|
||||||
peer->mgInfo->botStatus = botStatus;
|
peer->mgInfo->botStatus = botStatus;
|
||||||
if (App::main()) emit fullPeerUpdated(peer);
|
if (App::main()) emit fullPeerUpdated(peer);
|
||||||
}
|
}
|
||||||
|
@ -626,27 +634,33 @@ void ApiWrap::kickParticipant(PeerData *peer, UserData *user) {
|
||||||
void ApiWrap::kickParticipantDone(KickRequest kick, const MTPUpdates &result, mtpRequestId req) {
|
void ApiWrap::kickParticipantDone(KickRequest kick, const MTPUpdates &result, mtpRequestId req) {
|
||||||
_kickRequests.remove(kick);
|
_kickRequests.remove(kick);
|
||||||
if (kick.first->isMegagroup()) {
|
if (kick.first->isMegagroup()) {
|
||||||
int32 i = kick.first->asChannel()->mgInfo->lastParticipants.indexOf(kick.second);
|
auto channel = kick.first->asChannel();
|
||||||
|
auto megagroupInfo = channel->mgInfo;
|
||||||
|
|
||||||
|
int32 i = megagroupInfo->lastParticipants.indexOf(kick.second);
|
||||||
if (i >= 0) {
|
if (i >= 0) {
|
||||||
kick.first->asChannel()->mgInfo->lastParticipants.removeAt(i);
|
megagroupInfo->lastParticipants.removeAt(i);
|
||||||
}
|
}
|
||||||
if (kick.first->asChannel()->count > 1) {
|
|
||||||
--kick.first->asChannel()->count;
|
if (channel->membersCount() > 1) {
|
||||||
|
channel->setMembersCount(channel->membersCount() - 1);
|
||||||
} else {
|
} else {
|
||||||
kick.first->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
|
megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
|
||||||
kick.first->asChannel()->mgInfo->lastParticipantsCount = 0;
|
megagroupInfo->lastParticipantsCount = 0;
|
||||||
}
|
}
|
||||||
if (kick.first->asChannel()->mgInfo->lastAdmins.contains(kick.second)) {
|
if (megagroupInfo->lastAdmins.contains(kick.second)) {
|
||||||
kick.first->asChannel()->mgInfo->lastAdmins.remove(kick.second);
|
megagroupInfo->lastAdmins.remove(kick.second);
|
||||||
if (kick.first->asChannel()->adminsCount > 1) {
|
if (channel->adminsCount() > 1) {
|
||||||
--kick.first->asChannel()->adminsCount;
|
channel->setAdminsCount(channel->adminsCount() - 1);
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||||
}
|
}
|
||||||
kick.first->asChannel()->mgInfo->bots.remove(kick.second);
|
megagroupInfo->bots.remove(kick.second);
|
||||||
if (kick.first->asChannel()->mgInfo->bots.isEmpty() && kick.first->asChannel()->mgInfo->botStatus > 0) {
|
if (megagroupInfo->bots.isEmpty() && megagroupInfo->botStatus > 0) {
|
||||||
kick.first->asChannel()->mgInfo->botStatus = -1;
|
megagroupInfo->botStatus = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(kick.first, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
emit fullPeerUpdated(kick.first);
|
emit fullPeerUpdated(kick.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,6 +686,156 @@ void ApiWrap::requestStickerSets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApiWrap::joinChannel(ChannelData *channel) {
|
||||||
|
if (channel->amIn()) {
|
||||||
|
channelAmInUpdated(channel);
|
||||||
|
} else if (!_channelAmInRequests.contains(channel)) {
|
||||||
|
auto requestId = MTP::send(MTPchannels_JoinChannel(channel->inputChannel), rpcDone(&ApiWrap::channelAmInDone, channel), rpcFail(&ApiWrap::channelAmInFail, channel));
|
||||||
|
_channelAmInRequests.insert(channel, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::leaveChannel(ChannelData *channel) {
|
||||||
|
if (!channel->amIn()) {
|
||||||
|
channelAmInUpdated(channel);
|
||||||
|
} else if (!_channelAmInRequests.contains(channel)) {
|
||||||
|
auto requestId = MTP::send(MTPchannels_LeaveChannel(channel->inputChannel), rpcDone(&ApiWrap::channelAmInDone, channel), rpcFail(&ApiWrap::channelAmInFail, channel));
|
||||||
|
_channelAmInRequests.insert(channel, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::channelAmInUpdated(ChannelData *channel) {
|
||||||
|
Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelAmIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::channelAmInDone(ChannelData *channel, const MTPUpdates &updates) {
|
||||||
|
_channelAmInRequests.remove(channel);
|
||||||
|
|
||||||
|
updatesReceived(updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::channelAmInFail(ChannelData *channel, const RPCError &error) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
_channelAmInRequests.remove(channel);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::blockUser(UserData *user) {
|
||||||
|
if (user->isBlocked()) {
|
||||||
|
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserIsBlocked);
|
||||||
|
} else if (!_blockRequests.contains(user)) {
|
||||||
|
auto requestId = MTP::send(MTPcontacts_Block(user->inputUser), rpcDone(&ApiWrap::blockDone, user), rpcFail(&ApiWrap::blockFail, user));
|
||||||
|
_blockRequests.insert(user, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::unblockUser(UserData *user) {
|
||||||
|
if (!user->isBlocked()) {
|
||||||
|
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserIsBlocked);
|
||||||
|
} else if (!_blockRequests.contains(user)) {
|
||||||
|
auto requestId = MTP::send(MTPcontacts_Unblock(user->inputUser), rpcDone(&ApiWrap::unblockDone, user), rpcFail(&ApiWrap::blockFail, user));
|
||||||
|
_blockRequests.insert(user, requestId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::blockDone(UserData *user, const MTPBool &result) {
|
||||||
|
_blockRequests.remove(user);
|
||||||
|
user->setBlockStatus(UserData::BlockStatus::Blocked);
|
||||||
|
emit App::main()->peerUpdated(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::unblockDone(UserData *user, const MTPBool &result) {
|
||||||
|
_blockRequests.remove(user);
|
||||||
|
user->setBlockStatus(UserData::BlockStatus::NotBlocked);
|
||||||
|
emit App::main()->peerUpdated(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::blockFail(UserData *user, const RPCError &error) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
_blockRequests.remove(user);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::exportInviteLink(PeerData *peer) {
|
||||||
|
if (_exportInviteRequests.contains(peer)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mtpRequestId request = 0;
|
||||||
|
if (auto chat = peer->asChat()) {
|
||||||
|
request = MTP::send(MTPmessages_ExportChatInvite(chat->inputChat), rpcDone(&ApiWrap::exportInviteDone, peer), rpcFail(&ApiWrap::exportInviteFail, peer));
|
||||||
|
} else if (auto channel = peer->asChannel()) {
|
||||||
|
request = MTP::send(MTPchannels_ExportInvite(channel->inputChannel), rpcDone(&ApiWrap::exportInviteDone, peer), rpcFail(&ApiWrap::exportInviteFail, peer));
|
||||||
|
}
|
||||||
|
if (request) {
|
||||||
|
_exportInviteRequests.insert(peer, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::exportInviteDone(PeerData *peer, const MTPExportedChatInvite &result) {
|
||||||
|
_exportInviteRequests.remove(peer);
|
||||||
|
if (auto chat = peer->asChat()) {
|
||||||
|
chat->setInviteLink((result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString());
|
||||||
|
} else if (auto channel = peer->asChannel()) {
|
||||||
|
channel->setInviteLink((result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::exportInviteFail(PeerData *peer, const RPCError &error) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
_exportInviteRequests.remove(peer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::requestNotifySetting(PeerData *peer) {
|
||||||
|
if (_notifySettingRequests.contains(peer)) return;
|
||||||
|
|
||||||
|
MTPInputNotifyPeer notifyPeer = MTP_inputNotifyPeer(peer->input);
|
||||||
|
auto requestId = MTP::send(MTPaccount_GetNotifySettings(notifyPeer), rpcDone(&ApiWrap::notifySettingDone, notifyPeer), rpcFail(&ApiWrap::notifySettingFail, peer));
|
||||||
|
_notifySettingRequests.insert(peer, requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::notifySettingDone(MTPInputNotifyPeer notifyPeer, const MTPPeerNotifySettings &result) {
|
||||||
|
if (auto requestedPeer = notifySettingReceived(notifyPeer, result)) {
|
||||||
|
_notifySettingRequests.remove(requestedPeer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerData *ApiWrap::notifySettingReceived(MTPInputNotifyPeer notifyPeer, const MTPPeerNotifySettings &settings) {
|
||||||
|
PeerData *requestedPeer = nullptr;
|
||||||
|
switch (notifyPeer.type()) {
|
||||||
|
case mtpc_inputNotifyAll: App::main()->applyNotifySetting(MTP_notifyAll(), settings); break;
|
||||||
|
case mtpc_inputNotifyUsers: App::main()->applyNotifySetting(MTP_notifyUsers(), settings); break;
|
||||||
|
case mtpc_inputNotifyChats: App::main()->applyNotifySetting(MTP_notifyChats(), settings); break;
|
||||||
|
case mtpc_inputNotifyPeer: {
|
||||||
|
auto &peer = notifyPeer.c_inputNotifyPeer().vpeer;
|
||||||
|
switch (peer.type()) {
|
||||||
|
case mtpc_inputPeerEmpty: App::main()->applyNotifySetting(MTP_notifyPeer(MTP_peerUser(MTP_int(0))), settings); break;
|
||||||
|
case mtpc_inputPeerSelf: requestedPeer = App::self(); break;
|
||||||
|
case mtpc_inputPeerUser: requestedPeer = App::user(peerFromUser(peer.c_inputPeerUser().vuser_id)); break;
|
||||||
|
case mtpc_inputPeerChat: requestedPeer = App::chat(peerFromChat(peer.c_inputPeerChat().vchat_id)); break;
|
||||||
|
case mtpc_inputPeerChannel: requestedPeer = App::channel(peerFromChannel(peer.c_inputPeerChannel().vchannel_id)); break;
|
||||||
|
}
|
||||||
|
if (requestedPeer) {
|
||||||
|
App::main()->applyNotifySetting(MTP_notifyPeer(peerToMTP(requestedPeer->id)), settings);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
App::wnd()->notifySettingGot();
|
||||||
|
return requestedPeer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ApiWrap::notifySettingFail(PeerData *peer, const RPCError &error) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
notifySettingReceived(MTP_inputNotifyPeer(peer->input), MTP_peerNotifySettingsEmpty());
|
||||||
|
_notifySettingRequests.remove(peer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
|
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
|
||||||
_stickerSetRequests.remove(setId);
|
_stickerSetRequests.remove(setId);
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
ApiWrap(QObject *parent);
|
ApiWrap(QObject *parent);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
typedef SharedCallback<void, ChannelData*, MsgId> RequestMessageDataCallback;
|
using RequestMessageDataCallback = SharedCallback<void, ChannelData*, MsgId>;
|
||||||
void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
|
void requestMessageData(ChannelData *channel, MsgId msgId, std_::unique_ptr<RequestMessageDataCallback> callback);
|
||||||
|
|
||||||
void requestFullPeer(PeerData *peer);
|
void requestFullPeer(PeerData *peer);
|
||||||
|
@ -50,6 +50,15 @@ public:
|
||||||
void scheduleStickerSetRequest(uint64 setId, uint64 access);
|
void scheduleStickerSetRequest(uint64 setId, uint64 access);
|
||||||
void requestStickerSets();
|
void requestStickerSets();
|
||||||
|
|
||||||
|
void joinChannel(ChannelData *channel);
|
||||||
|
void leaveChannel(ChannelData *channel);
|
||||||
|
|
||||||
|
void blockUser(UserData *user);
|
||||||
|
void unblockUser(UserData *user);
|
||||||
|
|
||||||
|
void exportInviteLink(PeerData *peer);
|
||||||
|
void requestNotifySetting(PeerData *peer);
|
||||||
|
|
||||||
~ApiWrap();
|
~ApiWrap();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@ -65,6 +74,8 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void updatesReceived(const MTPUpdates &updates);
|
||||||
|
|
||||||
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
|
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
|
||||||
struct MessageDataRequest {
|
struct MessageDataRequest {
|
||||||
MessageDataRequest() : req(0) {
|
MessageDataRequest() : req(0) {
|
||||||
|
@ -120,4 +131,24 @@ private:
|
||||||
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
|
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
|
||||||
bool gotStickerSetFail(uint64 setId, const RPCError &error);
|
bool gotStickerSetFail(uint64 setId, const RPCError &error);
|
||||||
|
|
||||||
|
QMap<ChannelData*, mtpRequestId> _channelAmInRequests;
|
||||||
|
void channelAmInUpdated(ChannelData *channel);
|
||||||
|
void channelAmInDone(ChannelData *channel, const MTPUpdates &updates);
|
||||||
|
bool channelAmInFail(ChannelData *channel, const RPCError &error);
|
||||||
|
|
||||||
|
QMap<UserData*, mtpRequestId> _blockRequests;
|
||||||
|
void blockDone(UserData *user, const MTPBool &result);
|
||||||
|
void unblockDone(UserData *user, const MTPBool &result);
|
||||||
|
bool blockFail(UserData *user, const RPCError &error);
|
||||||
|
|
||||||
|
QMap<PeerData*, mtpRequestId> _exportInviteRequests;
|
||||||
|
void exportInviteDone(PeerData *peer, const MTPExportedChatInvite &result);
|
||||||
|
bool exportInviteFail(PeerData *peer, const RPCError &error);
|
||||||
|
|
||||||
|
QMap<PeerData*, mtpRequestId> _notifySettingRequests;
|
||||||
|
void notifySettingDone(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings);
|
||||||
|
PeerData *notifySettingReceived(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings);
|
||||||
|
bool notifySettingFail(PeerData *peer, const RPCError &error);
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "localstorage.h"
|
#include "localstorage.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "numbers.h"
|
#include "numbers.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
App::LaunchState _launchState = App::Launched;
|
App::LaunchState _launchState = App::Launched;
|
||||||
|
@ -222,11 +223,11 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 onlineForSort(UserData *user, int32 now) {
|
TimeId onlineForSort(UserData *user, TimeId now) {
|
||||||
if (isServiceUser(user->id) || user->botInfo) {
|
if (isServiceUser(user->id) || user->botInfo) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int32 online = user->onlineTill;
|
TimeId online = user->onlineTill;
|
||||||
if (online <= 0) {
|
if (online <= 0) {
|
||||||
switch (online) {
|
switch (online) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -252,11 +253,14 @@ namespace {
|
||||||
return online;
|
return online;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 onlineWillChangeIn(UserData *user, int32 now) {
|
int32 onlineWillChangeIn(UserData *user, TimeId now) {
|
||||||
if (isServiceUser(user->id) || user->botInfo) {
|
if (isServiceUser(user->id) || user->botInfo) {
|
||||||
return 86400;
|
return 86400;
|
||||||
}
|
}
|
||||||
int32 online = user->onlineTill;
|
return onlineWillChangeIn(user->onlineTill, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 onlineWillChangeIn(TimeId online, TimeId now) {
|
||||||
if (online <= 0) {
|
if (online <= 0) {
|
||||||
if (-online > now) return -online - now;
|
if (-online > now) return -online - now;
|
||||||
return 86400;
|
return 86400;
|
||||||
|
@ -276,7 +280,7 @@ namespace {
|
||||||
return dNow.secsTo(dTomorrow);
|
return dNow.secsTo(dTomorrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString onlineText(UserData *user, int32 now, bool precise) {
|
QString onlineText(UserData *user, TimeId now, bool precise) {
|
||||||
if (isNotificationsUser(user->id)) {
|
if (isNotificationsUser(user->id)) {
|
||||||
return lang(lng_status_service_notifications);
|
return lang(lng_status_service_notifications);
|
||||||
} else if (isServiceUser(user->id)) {
|
} else if (isServiceUser(user->id)) {
|
||||||
|
@ -284,7 +288,10 @@ namespace {
|
||||||
} else if (user->botInfo) {
|
} else if (user->botInfo) {
|
||||||
return lang(lng_status_bot);
|
return lang(lng_status_bot);
|
||||||
}
|
}
|
||||||
int32 online = user->onlineTill;
|
return onlineText(user->onlineTill, now, precise);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString onlineText(TimeId online, TimeId now, bool precise) {
|
||||||
if (online <= 0) {
|
if (online <= 0) {
|
||||||
switch (online) {
|
switch (online) {
|
||||||
case 0: return lang(lng_status_offline);
|
case 0: return lang(lng_status_offline);
|
||||||
|
@ -346,11 +353,14 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool onlineColorUse(UserData *user, int32 now) {
|
bool onlineColorUse(UserData *user, TimeId now) {
|
||||||
if (isServiceUser(user->id) || user->botInfo) {
|
if (isServiceUser(user->id) || user->botInfo) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int32 online = user->onlineTill;
|
return onlineColorUse(user->onlineTill, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onlineColorUse(TimeId online, TimeId now) {
|
||||||
if (online <= 0) {
|
if (online <= 0) {
|
||||||
switch (online) {
|
switch (online) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -364,21 +374,25 @@ namespace {
|
||||||
return (online > now);
|
return (online > now);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserData *feedUsers(const MTPVector<MTPUser> &users, bool emitPeerUpdated) {
|
UserData *feedUsers(const MTPVector<MTPUser> &users) {
|
||||||
UserData *data = 0;
|
UserData *result = nullptr;
|
||||||
const auto &v(users.c_vector().v);
|
for_const (auto &user, users.c_vector().v) {
|
||||||
for (QVector<MTPUser>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
UserData *data = nullptr;
|
||||||
const auto &user(*i);
|
|
||||||
data = 0;
|
|
||||||
bool wasContact = false, minimal = false;
|
bool wasContact = false, minimal = false;
|
||||||
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
|
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
|
||||||
|
|
||||||
|
Notify::PeerUpdate update;
|
||||||
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||||
|
|
||||||
switch (user.type()) {
|
switch (user.type()) {
|
||||||
case mtpc_userEmpty: {
|
case mtpc_userEmpty: {
|
||||||
const auto &d(user.c_userEmpty());
|
const auto &d(user.c_userEmpty());
|
||||||
|
|
||||||
PeerId peer(peerFromUser(d.vid.v));
|
PeerId peer(peerFromUser(d.vid.v));
|
||||||
data = App::user(peer);
|
data = App::user(peer);
|
||||||
|
auto canShareThisContact = data->canShareThisContactFast();
|
||||||
|
wasContact = data->isContact();
|
||||||
|
|
||||||
data->input = MTP_inputPeerUser(d.vid, MTP_long(0));
|
data->input = MTP_inputPeerUser(d.vid, MTP_long(0));
|
||||||
data->inputUser = MTP_inputUser(d.vid, MTP_long(0));
|
data->inputUser = MTP_inputUser(d.vid, MTP_long(0));
|
||||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||||
|
@ -386,9 +400,11 @@ namespace {
|
||||||
data->access = UserNoAccess;
|
data->access = UserNoAccess;
|
||||||
data->flags = 0;
|
data->flags = 0;
|
||||||
data->setBotInfoVersion(-1);
|
data->setBotInfoVersion(-1);
|
||||||
wasContact = (data->contact > 0);
|
|
||||||
status = &emptyStatus;
|
status = &emptyStatus;
|
||||||
data->contact = -1;
|
data->contact = -1;
|
||||||
|
|
||||||
|
if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact;
|
||||||
|
if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact;
|
||||||
} break;
|
} break;
|
||||||
case mtpc_user: {
|
case mtpc_user: {
|
||||||
const auto &d(user.c_user());
|
const auto &d(user.c_user());
|
||||||
|
@ -396,6 +412,8 @@ namespace {
|
||||||
|
|
||||||
PeerId peer(peerFromUser(d.vid.v));
|
PeerId peer(peerFromUser(d.vid.v));
|
||||||
data = App::user(peer);
|
data = App::user(peer);
|
||||||
|
auto canShareThisContact = data->canShareThisContactFast();
|
||||||
|
wasContact = data->isContact();
|
||||||
if (!minimal) {
|
if (!minimal) {
|
||||||
data->flags = d.vflags.v;
|
data->flags = d.vflags.v;
|
||||||
if (d.is_self()) {
|
if (d.is_self()) {
|
||||||
|
@ -415,7 +433,10 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (d.is_deleted()) {
|
if (d.is_deleted()) {
|
||||||
data->setPhone(QString());
|
if (!data->phone().isEmpty()) {
|
||||||
|
data->setPhone(QString());
|
||||||
|
update.flags |= UpdateFlag::UserPhoneChanged;
|
||||||
|
}
|
||||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||||
data->access = UserNoAccess;
|
data->access = UserNoAccess;
|
||||||
|
@ -427,12 +448,14 @@ namespace {
|
||||||
QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName;
|
QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName;
|
||||||
QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName;
|
QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName;
|
||||||
|
|
||||||
QString phone = minimal ? data->phone : (d.has_phone() ? qs(d.vphone) : QString());
|
QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString());
|
||||||
QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString());
|
QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString());
|
||||||
|
|
||||||
bool phoneChanged = (data->phone != phone);
|
bool phoneChanged = (data->phone() != phone);
|
||||||
if (phoneChanged) data->setPhone(phone);
|
if (phoneChanged) {
|
||||||
|
data->setPhone(phone);
|
||||||
|
update.flags |= UpdateFlag::UserPhoneChanged;
|
||||||
|
}
|
||||||
bool nameChanged = (data->firstName != fname) || (data->lastName != lname);
|
bool nameChanged = (data->firstName != fname) || (data->lastName != lname);
|
||||||
|
|
||||||
bool showPhone = !isServiceUser(data->id) && !d.is_self() && !d.is_contact() && !d.is_mutual_contact();
|
bool showPhone = !isServiceUser(data->id) && !d.is_self() && !d.is_contact() && !d.is_mutual_contact();
|
||||||
|
@ -458,7 +481,6 @@ namespace {
|
||||||
if (d.has_access_hash()) data->access = d.vaccess_hash.v;
|
if (d.has_access_hash()) data->access = d.vaccess_hash.v;
|
||||||
status = d.has_status() ? &d.vstatus : &emptyStatus;
|
status = d.has_status() ? &d.vstatus : &emptyStatus;
|
||||||
}
|
}
|
||||||
wasContact = (data->contact > 0);
|
|
||||||
if (!minimal) {
|
if (!minimal) {
|
||||||
if (d.has_bot_info_version()) {
|
if (d.has_bot_info_version()) {
|
||||||
data->setBotInfoVersion(d.vbot_info_version.v);
|
data->setBotInfoVersion(d.vbot_info_version.v);
|
||||||
|
@ -468,7 +490,7 @@ namespace {
|
||||||
} else {
|
} else {
|
||||||
data->setBotInfoVersion(-1);
|
data->setBotInfoVersion(-1);
|
||||||
}
|
}
|
||||||
data->contact = (d.is_contact() || d.is_mutual_contact()) ? 1 : (data->phone.isEmpty() ? -1 : 0);
|
data->contact = (d.is_contact() || d.is_mutual_contact()) ? 1 : (data->phone().isEmpty() ? -1 : 0);
|
||||||
if (data->contact == 1 && cReportSpamStatuses().value(data->id, dbiprsHidden) != dbiprsHidden) {
|
if (data->contact == 1 && cReportSpamStatuses().value(data->id, dbiprsHidden) != dbiprsHidden) {
|
||||||
cRefReportSpamStatuses().insert(data->id, dbiprsHidden);
|
cRefReportSpamStatuses().insert(data->id, dbiprsHidden);
|
||||||
Local::writeReportSpamStatuses();
|
Local::writeReportSpamStatuses();
|
||||||
|
@ -478,6 +500,9 @@ namespace {
|
||||||
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
if (App::wnd()) App::wnd()->updateGlobalMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact;
|
||||||
|
if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,6 +515,8 @@ namespace {
|
||||||
} else if (data->loadedStatus != PeerData::FullLoaded) {
|
} else if (data->loadedStatus != PeerData::FullLoaded) {
|
||||||
data->loadedStatus = PeerData::FullLoaded;
|
data->loadedStatus = PeerData::FullLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto oldOnlineTill = data->onlineTill;
|
||||||
if (status && !minimal) switch (status->type()) {
|
if (status && !minimal) switch (status->type()) {
|
||||||
case mtpc_userStatusEmpty: data->onlineTill = 0; break;
|
case mtpc_userStatusEmpty: data->onlineTill = 0; break;
|
||||||
case mtpc_userStatusRecently:
|
case mtpc_userStatusRecently:
|
||||||
|
@ -502,8 +529,11 @@ namespace {
|
||||||
case mtpc_userStatusOffline: data->onlineTill = status->c_userStatusOffline().vwas_online.v; break;
|
case mtpc_userStatusOffline: data->onlineTill = status->c_userStatusOffline().vwas_online.v; break;
|
||||||
case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break;
|
case mtpc_userStatusOnline: data->onlineTill = status->c_userStatusOnline().vexpires.v; break;
|
||||||
}
|
}
|
||||||
|
if (oldOnlineTill != data->onlineTill) {
|
||||||
|
update.flags |= UpdateFlag::UserOnlineChanged;
|
||||||
|
}
|
||||||
|
|
||||||
if (data->contact < 0 && !data->phone.isEmpty() && peerToUser(data->id) != MTP::authedId()) {
|
if (data->contact < 0 && !data->phone().isEmpty() && peerToUser(data->id) != MTP::authedId()) {
|
||||||
data->contact = 0;
|
data->contact = 0;
|
||||||
}
|
}
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
|
@ -511,34 +541,42 @@ namespace {
|
||||||
Notify::userIsContactChanged(data);
|
Notify::userIsContactChanged(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emitPeerUpdated) {
|
markPeerUpdated(data);
|
||||||
App::main()->peerUpdated(data);
|
if (update.flags) {
|
||||||
} else {
|
update.peer = data;
|
||||||
markPeerUpdated(data);
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
PeerData *feedChats(const MTPVector<MTPChat> &chats, bool emitPeerUpdated) {
|
PeerData *feedChats(const MTPVector<MTPChat> &chats) {
|
||||||
PeerData *data = 0;
|
PeerData *result = nullptr;
|
||||||
const auto &v(chats.c_vector().v);
|
for_const (auto &chat, chats.c_vector().v) {
|
||||||
for (QVector<MTPChat>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
PeerData *data = nullptr;
|
||||||
const auto &chat(*i);
|
|
||||||
data = 0;
|
|
||||||
bool minimal = false;
|
bool minimal = false;
|
||||||
|
|
||||||
|
Notify::PeerUpdate update;
|
||||||
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||||
|
|
||||||
switch (chat.type()) {
|
switch (chat.type()) {
|
||||||
case mtpc_chat: {
|
case mtpc_chat: {
|
||||||
const auto &d(chat.c_chat());
|
auto &d(chat.c_chat());
|
||||||
|
|
||||||
data = App::chat(peerFromChat(d.vid.v));
|
data = App::chat(peerFromChat(d.vid.v));
|
||||||
|
auto cdata = data->asChat();
|
||||||
|
auto canEdit = cdata->canEdit();
|
||||||
|
|
||||||
|
if (cdata->version < d.vversion.v) {
|
||||||
|
cdata->version = d.vversion.v;
|
||||||
|
cdata->invalidateParticipants();
|
||||||
|
}
|
||||||
|
|
||||||
data->input = MTP_inputPeerChat(d.vid);
|
data->input = MTP_inputPeerChat(d.vid);
|
||||||
|
cdata->setName(qs(d.vtitle));
|
||||||
data->updateName(qs(d.vtitle), QString(), QString());
|
|
||||||
|
|
||||||
ChatData *cdata = data->asChat();
|
|
||||||
cdata->setPhoto(d.vphoto);
|
cdata->setPhoto(d.vphoto);
|
||||||
cdata->date = d.vdate.v;
|
cdata->date = d.vdate.v;
|
||||||
|
|
||||||
|
@ -571,9 +609,11 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Notify::migrateUpdated(channel);
|
Notify::migrateUpdated(channel);
|
||||||
|
update.flags |= UpdateFlag::MigrationChanged;
|
||||||
}
|
}
|
||||||
if (updatedTo) {
|
if (updatedTo) {
|
||||||
Notify::migrateUpdated(cdata);
|
Notify::migrateUpdated(cdata);
|
||||||
|
update.flags |= UpdateFlag::MigrationChanged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,43 +624,52 @@ namespace {
|
||||||
|
|
||||||
cdata->count = d.vparticipants_count.v;
|
cdata->count = d.vparticipants_count.v;
|
||||||
cdata->isForbidden = false;
|
cdata->isForbidden = false;
|
||||||
if (cdata->version < d.vversion.v) {
|
if (canEdit != cdata->canEdit()) {
|
||||||
cdata->version = d.vversion.v;
|
update.flags |= UpdateFlag::ChatCanEdit;
|
||||||
cdata->invalidateParticipants();
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case mtpc_chatForbidden: {
|
case mtpc_chatForbidden: {
|
||||||
const auto &d(chat.c_chatForbidden());
|
auto &d(chat.c_chatForbidden());
|
||||||
|
|
||||||
data = App::chat(peerFromChat(d.vid.v));
|
data = App::chat(peerFromChat(d.vid.v));
|
||||||
|
auto cdata = data->asChat();
|
||||||
|
auto canEdit = cdata->canEdit();
|
||||||
|
|
||||||
data->input = MTP_inputPeerChat(d.vid);
|
data->input = MTP_inputPeerChat(d.vid);
|
||||||
|
cdata->setName(qs(d.vtitle));
|
||||||
data->updateName(qs(d.vtitle), QString(), QString());
|
|
||||||
|
|
||||||
ChatData *cdata = data->asChat();
|
|
||||||
cdata->setPhoto(MTP_chatPhotoEmpty());
|
cdata->setPhoto(MTP_chatPhotoEmpty());
|
||||||
cdata->date = 0;
|
cdata->date = 0;
|
||||||
cdata->count = -1;
|
cdata->count = -1;
|
||||||
cdata->invalidateParticipants();
|
cdata->invalidateParticipants();
|
||||||
cdata->flags = 0;
|
cdata->flags = 0;
|
||||||
cdata->isForbidden = true;
|
cdata->isForbidden = true;
|
||||||
|
if (canEdit != cdata->canEdit()) {
|
||||||
|
update.flags |= UpdateFlag::ChatCanEdit;
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case mtpc_channel: {
|
case mtpc_channel: {
|
||||||
const auto &d(chat.c_channel());
|
auto &d(chat.c_channel());
|
||||||
|
|
||||||
PeerId peer(peerFromChannel(d.vid.v));
|
auto peerId = peerFromChannel(d.vid.v);
|
||||||
minimal = d.is_min();
|
minimal = d.is_min();
|
||||||
if (minimal) {
|
if (minimal) {
|
||||||
data = App::channelLoaded(peer);
|
data = App::channelLoaded(peerId);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
continue; // minimal is not loaded, need to make getDifference
|
continue; // minimal is not loaded, need to make getDifference
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
data = App::channel(peer);
|
data = App::channel(peerId);
|
||||||
data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0));
|
data->input = MTP_inputPeerChannel(d.vid, d.has_access_hash() ? d.vaccess_hash : MTP_long(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelData *cdata = data->asChannel();
|
auto cdata = data->asChannel();
|
||||||
|
auto wasInChannel = cdata->amIn();
|
||||||
|
auto canEditPhoto = cdata->canEditPhoto();
|
||||||
|
auto canViewAdmins = cdata->canViewAdmins();
|
||||||
|
auto canViewMembers = cdata->canViewMembers();
|
||||||
|
auto canAddMembers = cdata->canAddMembers();
|
||||||
|
auto wasEditor = cdata->amEditor();
|
||||||
|
|
||||||
if (minimal) {
|
if (minimal) {
|
||||||
auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
|
auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
|
||||||
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
|
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
|
||||||
|
@ -644,15 +693,32 @@ namespace {
|
||||||
cdata->isForbidden = false;
|
cdata->isForbidden = false;
|
||||||
cdata->flagsUpdated();
|
cdata->flagsUpdated();
|
||||||
cdata->setPhoto(d.vphoto);
|
cdata->setPhoto(d.vphoto);
|
||||||
|
|
||||||
|
if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
|
||||||
|
if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto;
|
||||||
|
if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins;
|
||||||
|
if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers;
|
||||||
|
if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers;
|
||||||
|
if (wasEditor != cdata->amEditor()) {
|
||||||
|
cdata->selfAdminUpdated();
|
||||||
|
update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case mtpc_channelForbidden: {
|
case mtpc_channelForbidden: {
|
||||||
const auto &d(chat.c_channelForbidden());
|
auto &d(chat.c_channelForbidden());
|
||||||
|
|
||||||
PeerId peer(peerFromChannel(d.vid.v));
|
auto peerId = peerFromChannel(d.vid.v);
|
||||||
data = App::channel(peer);
|
data = App::channel(peerId);
|
||||||
data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash);
|
data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash);
|
||||||
|
|
||||||
ChannelData *cdata = data->asChannel();
|
auto cdata = data->asChannel();
|
||||||
|
auto wasInChannel = cdata->amIn();
|
||||||
|
auto canEditPhoto = cdata->canEditPhoto();
|
||||||
|
auto canViewAdmins = cdata->canViewAdmins();
|
||||||
|
auto canViewMembers = cdata->canViewMembers();
|
||||||
|
auto canAddMembers = cdata->canAddMembers();
|
||||||
|
auto wasEditor = cdata->amEditor();
|
||||||
|
|
||||||
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
||||||
|
|
||||||
auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
|
auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
|
||||||
|
@ -663,9 +729,19 @@ namespace {
|
||||||
cdata->access = d.vaccess_hash.v;
|
cdata->access = d.vaccess_hash.v;
|
||||||
cdata->setPhoto(MTP_chatPhotoEmpty());
|
cdata->setPhoto(MTP_chatPhotoEmpty());
|
||||||
cdata->date = 0;
|
cdata->date = 0;
|
||||||
cdata->count = 0;
|
cdata->setMembersCount(0);
|
||||||
cdata->isForbidden = true;
|
cdata->isForbidden = true;
|
||||||
cdata->flagsUpdated();
|
cdata->flagsUpdated();
|
||||||
|
|
||||||
|
if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
|
||||||
|
if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto;
|
||||||
|
if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins;
|
||||||
|
if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers;
|
||||||
|
if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers;
|
||||||
|
if (wasEditor != cdata->amEditor()) {
|
||||||
|
cdata->selfAdminUpdated();
|
||||||
|
update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
if (!data) continue;
|
if (!data) continue;
|
||||||
|
@ -678,14 +754,15 @@ namespace {
|
||||||
data->loadedStatus = PeerData::FullLoaded;
|
data->loadedStatus = PeerData::FullLoaded;
|
||||||
}
|
}
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
if (emitPeerUpdated) {
|
markPeerUpdated(data);
|
||||||
App::main()->peerUpdated(data);
|
if (update.flags) {
|
||||||
} else {
|
update.peer = data;
|
||||||
markPeerUpdated(data);
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result = data;
|
||||||
}
|
}
|
||||||
return data;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated) {
|
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated) {
|
||||||
|
@ -701,6 +778,7 @@ namespace {
|
||||||
case mtpc_chatParticipants: {
|
case mtpc_chatParticipants: {
|
||||||
const auto &d(p.c_chatParticipants());
|
const auto &d(p.c_chatParticipants());
|
||||||
chat = App::chat(d.vchat_id.v);
|
chat = App::chat(d.vchat_id.v);
|
||||||
|
auto canEdit = chat->canEdit();
|
||||||
if (!requestBotInfos || chat->version <= d.vversion.v) { // !requestBotInfos is true on getFullChat result
|
if (!requestBotInfos || chat->version <= d.vversion.v) { // !requestBotInfos is true on getFullChat result
|
||||||
chat->version = d.vversion.v;
|
chat->version = d.vversion.v;
|
||||||
const auto &v(d.vparticipants.c_vector().v);
|
const auto &v(d.vparticipants.c_vector().v);
|
||||||
|
@ -772,8 +850,12 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (canEdit != chat->canEdit()) {
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::ChatCanEdit);
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged | Notify::PeerUpdate::Flag::AdminsChanged);
|
||||||
if (chat && App::main()) {
|
if (chat && App::main()) {
|
||||||
if (emitPeerUpdated) {
|
if (emitPeerUpdated) {
|
||||||
App::main()->peerUpdated(chat);
|
App::main()->peerUpdated(chat);
|
||||||
|
@ -820,6 +902,7 @@ namespace {
|
||||||
chat->invalidateParticipants();
|
chat->invalidateParticipants();
|
||||||
chat->count++;
|
chat->count++;
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
if (emitPeerUpdated) {
|
if (emitPeerUpdated) {
|
||||||
App::main()->peerUpdated(chat);
|
App::main()->peerUpdated(chat);
|
||||||
|
@ -845,6 +928,7 @@ namespace {
|
||||||
}
|
}
|
||||||
} else if (chat->version <= d.vversion.v && chat->count > 0) {
|
} else if (chat->version <= d.vversion.v && chat->count > 0) {
|
||||||
chat->version = d.vversion.v;
|
chat->version = d.vversion.v;
|
||||||
|
auto canEdit = chat->canEdit();
|
||||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||||
if (user) {
|
if (user) {
|
||||||
if (chat->participants.isEmpty()) {
|
if (chat->participants.isEmpty()) {
|
||||||
|
@ -886,6 +970,10 @@ namespace {
|
||||||
chat->invalidateParticipants();
|
chat->invalidateParticipants();
|
||||||
chat->count--;
|
chat->count--;
|
||||||
}
|
}
|
||||||
|
if (canEdit != chat->canEdit()) {
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::ChatCanEdit);
|
||||||
|
}
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
if (emitPeerUpdated) {
|
if (emitPeerUpdated) {
|
||||||
App::main()->peerUpdated(chat);
|
App::main()->peerUpdated(chat);
|
||||||
|
@ -906,13 +994,14 @@ namespace {
|
||||||
}
|
}
|
||||||
chat->version = d.vversion.v;
|
chat->version = d.vversion.v;
|
||||||
if (mtpIsTrue(d.venabled)) {
|
if (mtpIsTrue(d.venabled)) {
|
||||||
chat->flags |= MTPDchat::Flag::f_admins_enabled;
|
|
||||||
if (!badVersion) {
|
if (!badVersion) {
|
||||||
chat->invalidateParticipants();
|
chat->invalidateParticipants();
|
||||||
}
|
}
|
||||||
|
chat->flags |= MTPDchat::Flag::f_admins_enabled;
|
||||||
} else {
|
} else {
|
||||||
chat->flags &= ~MTPDchat::Flag::f_admins_enabled;
|
chat->flags &= ~MTPDchat::Flag::f_admins_enabled;
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||||
if (emitPeerUpdated) {
|
if (emitPeerUpdated) {
|
||||||
App::main()->peerUpdated(chat);
|
App::main()->peerUpdated(chat);
|
||||||
} else {
|
} else {
|
||||||
|
@ -936,6 +1025,7 @@ namespace {
|
||||||
}
|
}
|
||||||
} else if (chat->version <= d.vversion.v && chat->count > 0) {
|
} else if (chat->version <= d.vversion.v && chat->count > 0) {
|
||||||
chat->version = d.vversion.v;
|
chat->version = d.vversion.v;
|
||||||
|
auto canEdit = chat->canEdit();
|
||||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||||
if (user) {
|
if (user) {
|
||||||
if (mtpIsTrue(d.vis_admin)) {
|
if (mtpIsTrue(d.vis_admin)) {
|
||||||
|
@ -956,6 +1046,10 @@ namespace {
|
||||||
} else {
|
} else {
|
||||||
chat->invalidateParticipants();
|
chat->invalidateParticipants();
|
||||||
}
|
}
|
||||||
|
if (canEdit != chat->canEdit()) {
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::ChatCanEdit);
|
||||||
|
}
|
||||||
|
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||||
if (App::main()) {
|
if (App::main()) {
|
||||||
if (emitPeerUpdated) {
|
if (emitPeerUpdated) {
|
||||||
App::main()->peerUpdated(chat);
|
App::main()->peerUpdated(chat);
|
||||||
|
@ -1163,33 +1257,10 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links, bool emitPeerUpdated) {
|
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink) {
|
||||||
const auto &v(links.c_vector().v);
|
|
||||||
for (QVector<MTPcontacts_Link>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
|
||||||
const auto &dv(i->c_contacts_link());
|
|
||||||
UserData *user = feedUsers(MTP_vector<MTPUser>(1, dv.vuser), false);
|
|
||||||
MTPint userId(MTP_int(0));
|
|
||||||
switch (dv.vuser.type()) {
|
|
||||||
case mtpc_userEmpty: userId = dv.vuser.c_userEmpty().vid; break;
|
|
||||||
case mtpc_user: userId = dv.vuser.c_user().vid; break;
|
|
||||||
}
|
|
||||||
if (userId.v) {
|
|
||||||
feedUserLink(userId, dv.vmy_link, dv.vforeign_link, false);
|
|
||||||
}
|
|
||||||
if (user && App::main()) {
|
|
||||||
if (emitPeerUpdated) {
|
|
||||||
App::main()->peerUpdated(user);
|
|
||||||
} else {
|
|
||||||
markPeerUpdated(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink, bool emitPeerUpdated) {
|
|
||||||
UserData *user = userLoaded(userId.v);
|
UserData *user = userLoaded(userId.v);
|
||||||
if (user) {
|
if (user) {
|
||||||
bool wasContact = (user->contact > 0);
|
auto wasContact = user->isContact();
|
||||||
bool wasShowPhone = !user->contact;
|
bool wasShowPhone = !user->contact;
|
||||||
switch (myLink.type()) {
|
switch (myLink.type()) {
|
||||||
case mtpc_contactLinkContact:
|
case mtpc_contactLinkContact:
|
||||||
|
@ -1208,10 +1279,14 @@ namespace {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (user->contact < 1) {
|
if (user->contact < 1) {
|
||||||
if (user->contact < 0 && !user->phone.isEmpty() && peerToUser(user->id) != MTP::authedId()) {
|
if (user->contact < 0 && !user->phone().isEmpty() && peerToUser(user->id) != MTP::authedId()) {
|
||||||
user->contact = 0;
|
user->contact = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (wasContact != user->isContact()) {
|
||||||
|
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserIsContact);
|
||||||
|
}
|
||||||
if ((user->contact > 0 && !wasContact) || (wasContact && user->contact < 1)) {
|
if ((user->contact > 0 && !wasContact) || (wasContact && user->contact < 1)) {
|
||||||
Notify::userIsContactChanged(user);
|
Notify::userIsContactChanged(user);
|
||||||
}
|
}
|
||||||
|
@ -1219,15 +1294,9 @@ namespace {
|
||||||
bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
|
bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
|
||||||
bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
|
bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
|
||||||
if (showPhoneChanged) {
|
if (showPhoneChanged) {
|
||||||
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone) : QString(), textOneLine(user->username));
|
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), textOneLine(user->username));
|
||||||
}
|
|
||||||
if (App::main()) {
|
|
||||||
if (emitPeerUpdated) {
|
|
||||||
App::main()->peerUpdated(user);
|
|
||||||
} else {
|
|
||||||
markPeerUpdated(user);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
markPeerUpdated(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2272,6 +2341,10 @@ namespace {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QPixmap pixmapFromImageInPlace(QImage &&image) {
|
||||||
|
return QPixmap::fromImage(std_::forward<QImage>(image), Qt::ColorOnly);
|
||||||
|
}
|
||||||
|
|
||||||
void regPhotoItem(PhotoData *data, HistoryItem *item) {
|
void regPhotoItem(PhotoData *data, HistoryItem *item) {
|
||||||
::photoItems[data].insert(item, NullType());
|
::photoItems[data].insert(item, NullType());
|
||||||
}
|
}
|
||||||
|
@ -2317,11 +2390,21 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
void regSharedContactItem(int32 userId, HistoryItem *item) {
|
void regSharedContactItem(int32 userId, HistoryItem *item) {
|
||||||
|
auto user = App::userLoaded(userId);
|
||||||
|
auto canShareThisContact = user ? user->canShareThisContact() : false;
|
||||||
::sharedContactItems[userId].insert(item, NullType());
|
::sharedContactItems[userId].insert(item, NullType());
|
||||||
|
if (canShareThisContact != (user ? user->canShareThisContact() : false)) {
|
||||||
|
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserCanShareContact);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregSharedContactItem(int32 userId, HistoryItem *item) {
|
void unregSharedContactItem(int32 userId, HistoryItem *item) {
|
||||||
|
auto user = App::userLoaded(userId);
|
||||||
|
auto canShareThisContact = user ? user->canShareThisContact() : false;
|
||||||
::sharedContactItems[userId].remove(item);
|
::sharedContactItems[userId].remove(item);
|
||||||
|
if (canShareThisContact != (user ? user->canShareThisContact() : false)) {
|
||||||
|
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserCanShareContact);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SharedContactItems &sharedContactItems() {
|
const SharedContactItems &sharedContactItems() {
|
||||||
|
|
|
@ -59,13 +59,17 @@ namespace App {
|
||||||
|
|
||||||
QString formatPhone(QString phone);
|
QString formatPhone(QString phone);
|
||||||
|
|
||||||
int32 onlineForSort(UserData *user, int32 now);
|
TimeId onlineForSort(UserData *user, TimeId now);
|
||||||
int32 onlineWillChangeIn(UserData *user, int32 nowOnServer);
|
int32 onlineWillChangeIn(UserData *user, TimeId now);
|
||||||
QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
|
int32 onlineWillChangeIn(TimeId online, TimeId now);
|
||||||
bool onlineColorUse(UserData *user, int32 now);
|
QString onlineText(UserData *user, TimeId now, bool precise = false);
|
||||||
|
QString onlineText(TimeId online, TimeId now, bool precise = false);
|
||||||
|
bool onlineColorUse(UserData *user, TimeId now);
|
||||||
|
bool onlineColorUse(TimeId online, TimeId now);
|
||||||
|
|
||||||
|
UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
|
||||||
|
PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
|
||||||
|
|
||||||
UserData *feedUsers(const MTPVector<MTPUser> &users, bool emitPeerUpdated = true); // returns last user
|
|
||||||
PeerData *feedChats(const MTPVector<MTPChat> &chats, bool emitPeerUpdated = true); // returns last chat
|
|
||||||
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated = true);
|
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated = true);
|
||||||
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d, bool emitPeerUpdated = true);
|
void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d, bool emitPeerUpdated = true);
|
||||||
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d, bool emitPeerUpdated = true);
|
void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d, bool emitPeerUpdated = true);
|
||||||
|
@ -80,12 +84,10 @@ namespace App {
|
||||||
void feedInboxRead(const PeerId &peer, MsgId upTo);
|
void feedInboxRead(const PeerId &peer, MsgId upTo);
|
||||||
void feedOutboxRead(const PeerId &peer, MsgId upTo);
|
void feedOutboxRead(const PeerId &peer, MsgId upTo);
|
||||||
void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds);
|
void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds);
|
||||||
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links, bool emitPeerUpdated = true);
|
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
|
||||||
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink, bool emitPeerUpdated = true);
|
|
||||||
|
|
||||||
void markPeerUpdated(PeerData *data);
|
void markPeerUpdated(PeerData *data);
|
||||||
void clearPeerUpdated(PeerData *data);
|
void clearPeerUpdated(PeerData *data);
|
||||||
void emitPeerUpdated();
|
|
||||||
|
|
||||||
ImagePtr image(const MTPPhotoSize &size);
|
ImagePtr image(const MTPPhotoSize &size);
|
||||||
StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc);
|
StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc);
|
||||||
|
@ -231,6 +233,7 @@ namespace App {
|
||||||
|
|
||||||
QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
|
QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
|
||||||
QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0);
|
QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0);
|
||||||
|
QPixmap pixmapFromImageInPlace(QImage &&image);
|
||||||
|
|
||||||
void regPhotoItem(PhotoData *data, HistoryItem *item);
|
void regPhotoItem(PhotoData *data, HistoryItem *item);
|
||||||
void unregPhotoItem(PhotoData *data, HistoryItem *item);
|
void unregPhotoItem(PhotoData *data, HistoryItem *item);
|
||||||
|
|
|
@ -27,9 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
#include "boxes/confirmbox.h"
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "ui/filedialog.h"
|
||||||
#include "langloaderplain.h"
|
#include "langloaderplain.h"
|
||||||
#include "localstorage.h"
|
#include "localstorage.h"
|
||||||
#include "autoupdater.h"
|
#include "autoupdater.h"
|
||||||
|
#include "core/observer.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void mtpStateChanged(int32 dc, int32 state) {
|
void mtpStateChanged(int32 dc, int32 state) {
|
||||||
|
@ -200,6 +203,7 @@ void Application::singleInstanceChecked() {
|
||||||
Logs::multipleInstances();
|
Logs::multipleInstances();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Notify::startObservers();
|
||||||
Sandbox::start();
|
Sandbox::start();
|
||||||
|
|
||||||
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
|
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
|
||||||
|
@ -336,6 +340,8 @@ void Application::closeApplication() {
|
||||||
if (_updateThread) _updateThread->quit();
|
if (_updateThread) _updateThread->quit();
|
||||||
_updateThread = 0;
|
_updateThread = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
Notify::finishObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||||
|
@ -902,6 +908,18 @@ void AppClass::call_handleUnreadCounterUpdate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppClass::call_handleFileDialogQueue() {
|
||||||
|
while (true) {
|
||||||
|
if (!FileDialog::processQuery()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppClass::call_handleDelayedPeerUpdates() {
|
||||||
|
Notify::peerUpdatedSendDelayed();
|
||||||
|
}
|
||||||
|
|
||||||
void AppClass::killDownloadSessions() {
|
void AppClass::killDownloadSessions() {
|
||||||
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
|
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
|
||||||
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
|
||||||
|
|
|
@ -203,6 +203,8 @@ public slots:
|
||||||
|
|
||||||
void call_handleHistoryUpdate();
|
void call_handleHistoryUpdate();
|
||||||
void call_handleUnreadCounterUpdate();
|
void call_handleUnreadCounterUpdate();
|
||||||
|
void call_handleFileDialogQueue();
|
||||||
|
void call_handleDelayedPeerUpdates();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
AboutBox::AboutBox() : AbstractBox(st::aboutWidth)
|
AboutBox::AboutBox() : AbstractBox(st::aboutWidth)
|
||||||
, _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink)
|
, _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink)
|
||||||
, _text1(this, lang(lng_about_text_1), st::aboutLabel, st::aboutTextStyle)
|
, _text1(this, lang(lng_about_text_1), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle)
|
||||||
, _text2(this, lang(lng_about_text_2), st::aboutLabel, st::aboutTextStyle)
|
, _text2(this, lang(lng_about_text_2), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle)
|
||||||
, _text3(this, QString(), st::aboutLabel, st::aboutTextStyle)
|
, _text3(this,st::aboutLabel, st::aboutTextStyle)
|
||||||
, _done(this, lang(lng_close), st::defaultBoxButton) {
|
, _done(this, lang(lng_close), st::defaultBoxButton) {
|
||||||
_text3.setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]")));
|
_text3.setRichText(lng_about_text_3(lt_faq_open, qsl("[a href=\"%1\"]").arg(telegramFaqLink()), lt_faq_close, qsl("[/a]")));
|
||||||
|
|
||||||
|
@ -109,10 +109,9 @@ void AboutBox::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
|
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
|
||||||
QString _getCrashReportFile(const QMimeData *m) {
|
QString _getCrashReportFile(const QMimeData *m) {
|
||||||
if (!m || m->urls().size() != 1) return QString();
|
if (!m || m->urls().size() != 1 || !m->urls().at(0).isLocalFile()) return QString();
|
||||||
|
|
||||||
QString file(m->urls().at(0).toLocalFile());
|
auto file = psConvertFileUrl(m->urls().at(0));
|
||||||
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
|
|
||||||
|
|
||||||
return file.endsWith(qstr(".telegramcrash"), Qt::CaseInsensitive) ? file : QString();
|
return file.endsWith(qstr(".telegramcrash"), Qt::CaseInsensitive) ? file : QString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "abstractbox.h"
|
#include "abstractbox.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
|
|
||||||
class AboutBox : public AbstractBox {
|
class AboutBox : public AbstractBox {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
|
@ -30,18 +30,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth)
|
AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth)
|
||||||
, _user(0)
|
|
||||||
, _save(this, lang(lng_add_contact), st::defaultBoxButton)
|
, _save(this, lang(lng_add_contact), st::defaultBoxButton)
|
||||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
||||||
, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton)
|
, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton)
|
||||||
, _first(this, st::defaultInputField, lang(lng_signup_firstname), fname)
|
, _first(this, st::defaultInputField, lang(lng_signup_firstname), fname)
|
||||||
, _last(this, st::defaultInputField, lang(lng_signup_lastname), lname)
|
, _last(this, st::defaultInputField, lang(lng_signup_lastname), lname)
|
||||||
, _phone(this, st::defaultInputField, lang(lng_contact_phone), phone)
|
, _phone(this, st::defaultInputField, lang(lng_contact_phone), phone)
|
||||||
, _invertOrder(langFirstNameGoesSecond())
|
, _invertOrder(langFirstNameGoesSecond()) {
|
||||||
, _contactId(0)
|
|
||||||
, _addRequest(0) {
|
|
||||||
if (!phone.isEmpty()) {
|
if (!phone.isEmpty()) {
|
||||||
_phone.setDisabled(true);
|
_phone.setDisabled(true);
|
||||||
}
|
}
|
||||||
|
@ -56,10 +54,8 @@ AddContactBox::AddContactBox(UserData *user) : AbstractBox(st::boxWidth)
|
||||||
, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton)
|
, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton)
|
||||||
, _first(this, st::defaultInputField, lang(lng_signup_firstname), user->firstName)
|
, _first(this, st::defaultInputField, lang(lng_signup_firstname), user->firstName)
|
||||||
, _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName)
|
, _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName)
|
||||||
, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone)
|
, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone())
|
||||||
, _invertOrder(langFirstNameGoesSecond())
|
, _invertOrder(langFirstNameGoesSecond()) {
|
||||||
, _contactId(0)
|
|
||||||
, _addRequest(0) {
|
|
||||||
_phone.setDisabled(true);
|
_phone.setDisabled(true);
|
||||||
initBox();
|
initBox();
|
||||||
}
|
}
|
||||||
|
@ -190,7 +186,7 @@ void AddContactBox::onSave() {
|
||||||
_sentName = firstName;
|
_sentName = firstName;
|
||||||
if (_user) {
|
if (_user) {
|
||||||
_contactId = rand_value<uint64>();
|
_contactId = rand_value<uint64>();
|
||||||
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone), MTP_string(firstName), MTP_string(lastName)));
|
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone()), MTP_string(firstName), MTP_string(lastName)));
|
||||||
_addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector<MTPInputContact>(v), MTP_bool(false)), rpcDone(&AddContactBox::onSaveUserDone), rpcFail(&AddContactBox::onSaveUserFail));
|
_addRequest = MTP::send(MTPcontacts_ImportContacts(MTP_vector<MTPInputContact>(v), MTP_bool(false)), rpcDone(&AddContactBox::onSaveUserDone), rpcFail(&AddContactBox::onSaveUserFail));
|
||||||
} else {
|
} else {
|
||||||
_contactId = rand_value<uint64>();
|
_contactId = rand_value<uint64>();
|
||||||
|
@ -206,7 +202,7 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
|
||||||
QString err(error.type());
|
QString err(error.type());
|
||||||
QString firstName = _first.getLastText().trimmed(), lastName = _last.getLastText().trimmed();
|
QString firstName = _first.getLastText().trimmed(), lastName = _last.getLastText().trimmed();
|
||||||
if (err == "CHAT_TITLE_NOT_MODIFIED") {
|
if (err == "CHAT_TITLE_NOT_MODIFIED") {
|
||||||
_user->updateName(firstName, QString(), QString());
|
_user->setName(firstName, lastName, _user->nameOrPhone, _user->username);
|
||||||
emit closed();
|
emit closed();
|
||||||
return true;
|
return true;
|
||||||
} else if (err == "NO_CHAT_TITLE") {
|
} else if (err == "NO_CHAT_TITLE") {
|
||||||
|
@ -548,7 +544,7 @@ bool GroupInfoBox::creationFail(const RPCError &error) {
|
||||||
void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
|
void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
|
||||||
_creationRequestId = 0;
|
_creationRequestId = 0;
|
||||||
if (result.type() == mtpc_chatInviteExported) {
|
if (result.type() == mtpc_chatInviteExported) {
|
||||||
_createdChannel->invitationUrl = qs(result.c_chatInviteExported().vlink);
|
_createdChannel->setInviteLink(qs(result.c_chatInviteExported().vlink));
|
||||||
}
|
}
|
||||||
Ui::showLayer(new SetupChannelBox(_createdChannel));
|
Ui::showLayer(new SetupChannelBox(_createdChannel));
|
||||||
}
|
}
|
||||||
|
@ -712,7 +708,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) {
|
||||||
option.setWrapMode(QTextOption::WrapAnywhere);
|
option.setWrapMode(QTextOption::WrapAnywhere);
|
||||||
p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont);
|
p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont);
|
||||||
p.setPen(st::btnDefLink.color);
|
p.setPen(st::btnDefLink.color);
|
||||||
p.drawText(_invitationLink, _channel->invitationUrl, option);
|
p.drawText(_invitationLink, _channel->inviteLink(), option);
|
||||||
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) {
|
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) {
|
||||||
p.setOpacity(a_goodOpacity.current());
|
p.setOpacity(a_goodOpacity.current());
|
||||||
p.setPen(st::setGoodColor);
|
p.setPen(st::setGoodColor);
|
||||||
|
@ -753,7 +749,7 @@ void SetupChannelBox::mouseMoveEvent(QMouseEvent *e) {
|
||||||
void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
|
void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
|
||||||
mouseMoveEvent(e);
|
mouseMoveEvent(e);
|
||||||
if (_linkOver) {
|
if (_linkOver) {
|
||||||
Application::clipboard()->setText(_channel->invitationUrl);
|
Application::clipboard()->setText(_channel->inviteLink());
|
||||||
_goodTextLink = lang(lng_create_channel_link_copied);
|
_goodTextLink = lang(lng_create_channel_link_copied);
|
||||||
a_goodOpacity = anim::fvalue(1, 0);
|
a_goodOpacity = anim::fvalue(1, 0);
|
||||||
_a_goodFade.start();
|
_a_goodFade.start();
|
||||||
|
@ -1140,7 +1136,9 @@ bool EditNameTitleBox::onSaveChatFail(const RPCError &error) {
|
||||||
_requestId = 0;
|
_requestId = 0;
|
||||||
QString err(error.type());
|
QString err(error.type());
|
||||||
if (err == qstr("CHAT_TITLE_NOT_MODIFIED") || err == qstr("CHAT_NOT_MODIFIED")) {
|
if (err == qstr("CHAT_TITLE_NOT_MODIFIED") || err == qstr("CHAT_NOT_MODIFIED")) {
|
||||||
_peer->updateName(_sentName, QString(), QString());
|
if (auto chatData = _peer->asChat()) {
|
||||||
|
chatData->setName(_sentName);
|
||||||
|
}
|
||||||
emit closed();
|
emit closed();
|
||||||
return true;
|
return true;
|
||||||
} else if (err == qstr("NO_CHAT_TITLE")) {
|
} else if (err == qstr("NO_CHAT_TITLE")) {
|
||||||
|
@ -1162,7 +1160,7 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox()
|
||||||
, _save(this, lang(lng_settings_save), st::defaultBoxButton)
|
, _save(this, lang(lng_settings_save), st::defaultBoxButton)
|
||||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
||||||
, _title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name)
|
, _title(this, st::defaultInputField, lang(lng_dlg_new_channel_name), _channel->name)
|
||||||
, _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about)
|
, _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about())
|
||||||
, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature())
|
, _sign(this, lang(lng_edit_sign_messages), channel->addsSignature())
|
||||||
, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton)
|
, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton)
|
||||||
, _saveTitleRequestId(0)
|
, _saveTitleRequestId(0)
|
||||||
|
@ -1303,7 +1301,7 @@ void EditChannelBox::onPublicLink() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditChannelBox::saveDescription() {
|
void EditChannelBox::saveDescription() {
|
||||||
if (_sentDescription == _channel->about) {
|
if (_sentDescription == _channel->about()) {
|
||||||
saveSign();
|
saveSign();
|
||||||
} else {
|
} else {
|
||||||
_saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail));
|
_saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail));
|
||||||
|
@ -1338,9 +1336,10 @@ bool EditChannelBox::onSaveFail(const RPCError &error, mtpRequestId req) {
|
||||||
} else if (req == _saveDescriptionRequestId) {
|
} else if (req == _saveDescriptionRequestId) {
|
||||||
_saveDescriptionRequestId = 0;
|
_saveDescriptionRequestId = 0;
|
||||||
if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
|
if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
|
||||||
_channel->about = _sentDescription;
|
if (_channel->setAbout(_sentDescription)) {
|
||||||
if (App::api()) {
|
if (App::api()) {
|
||||||
emit App::api()->fullPeerUpdated(_channel);
|
emit App::api()->fullPeerUpdated(_channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
saveSign();
|
saveSign();
|
||||||
return true;
|
return true;
|
||||||
|
@ -1367,9 +1366,10 @@ void EditChannelBox::onSaveTitleDone(const MTPUpdates &updates) {
|
||||||
|
|
||||||
void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) {
|
void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) {
|
||||||
_saveDescriptionRequestId = 0;
|
_saveDescriptionRequestId = 0;
|
||||||
_channel->about = _sentDescription;
|
if (_channel->setAbout(_sentDescription)) {
|
||||||
if (App::api()) {
|
if (App::api()) {
|
||||||
emit App::api()->fullPeerUpdated(_channel);
|
emit App::api()->fullPeerUpdated(_channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
saveSign();
|
saveSign();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ private:
|
||||||
|
|
||||||
void initBox();
|
void initBox();
|
||||||
|
|
||||||
UserData *_user;
|
UserData *_user = nullptr;
|
||||||
QString _boxTitle;
|
QString _boxTitle;
|
||||||
|
|
||||||
BoxButton _save, _cancel, _retry;
|
BoxButton _save, _cancel, _retry;
|
||||||
|
@ -66,9 +66,9 @@ private:
|
||||||
|
|
||||||
bool _invertOrder;
|
bool _invertOrder;
|
||||||
|
|
||||||
uint64 _contactId;
|
uint64 _contactId = 0;
|
||||||
|
|
||||||
mtpRequestId _addRequest;
|
mtpRequestId _addRequest = 0;
|
||||||
QString _sentName;
|
QString _sentName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -381,11 +381,10 @@ void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) {
|
||||||
PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth)
|
PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth)
|
||||||
, _channel(channel)
|
, _channel(channel)
|
||||||
, _msgId(msgId)
|
, _msgId(msgId)
|
||||||
, _text(this, lang(lng_pinned_pin_sure), st::boxLabel)
|
, _text(this, lang(lng_pinned_pin_sure), FlatLabel::InitType::Simple, st::boxLabel)
|
||||||
, _notify(this, lang(lng_pinned_notify), true)
|
, _notify(this, lang(lng_pinned_notify), true)
|
||||||
, _pin(this, lang(lng_pinned_pin), st::defaultBoxButton)
|
, _pin(this, lang(lng_pinned_pin), st::defaultBoxButton)
|
||||||
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
|
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
|
||||||
, _requestId(0) {
|
|
||||||
_text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right());
|
_text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right());
|
||||||
setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _notify.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin.height() + st::boxButtonPadding.bottom());
|
setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _notify.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin.height() + st::boxButtonPadding.bottom());
|
||||||
|
|
||||||
|
@ -441,7 +440,7 @@ RichDeleteMessageBox::RichDeleteMessageBox(ChannelData *channel, UserData *from,
|
||||||
, _channel(channel)
|
, _channel(channel)
|
||||||
, _from(from)
|
, _from(from)
|
||||||
, _msgId(msgId)
|
, _msgId(msgId)
|
||||||
, _text(this, lang(lng_selected_delete_sure_this), st::boxLabel)
|
, _text(this, lang(lng_selected_delete_sure_this), FlatLabel::InitType::Simple, st::boxLabel)
|
||||||
, _banUser(this, lang(lng_ban_user), false)
|
, _banUser(this, lang(lng_ban_user), false)
|
||||||
, _reportSpam(this, lang(lng_report_spam), false)
|
, _reportSpam(this, lang(lng_report_spam), false)
|
||||||
, _deleteAll(this, lang(lng_delete_all_from), false)
|
, _deleteAll(this, lang(lng_delete_all_from), false)
|
||||||
|
@ -478,6 +477,7 @@ void RichDeleteMessageBox::onDelete() {
|
||||||
if (HistoryItem *item = App::histItemById(_channel ? peerToChannel(_channel->id) : 0, _msgId)) {
|
if (HistoryItem *item = App::histItemById(_channel ? peerToChannel(_channel->id) : 0, _msgId)) {
|
||||||
bool wasLast = (item->history()->lastMsg == item);
|
bool wasLast = (item->history()->lastMsg == item);
|
||||||
item->destroy();
|
item->destroy();
|
||||||
|
|
||||||
if (_msgId > 0) {
|
if (_msgId > 0) {
|
||||||
App::main()->deleteMessages(_channel, QVector<MTPint>(1, MTP_int(_msgId)));
|
App::main()->deleteMessages(_channel, QVector<MTPint>(1, MTP_int(_msgId)));
|
||||||
} else if (wasLast) {
|
} else if (wasLast) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "abstractbox.h"
|
#include "abstractbox.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
|
|
||||||
class InformBox;
|
class InformBox;
|
||||||
class ConfirmBox : public AbstractBox, public ClickHandlerHost {
|
class ConfirmBox : public AbstractBox, public ClickHandlerHost {
|
||||||
|
@ -216,7 +217,7 @@ private:
|
||||||
|
|
||||||
BoxButton _pin, _cancel;
|
BoxButton _pin, _cancel;
|
||||||
|
|
||||||
mtpRequestId _requestId;
|
mtpRequestId _requestId = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "ui/filedialog.h"
|
#include "ui/filedialog.h"
|
||||||
#include "boxes/photocropbox.h"
|
#include "boxes/photocropbox.h"
|
||||||
#include "boxes/confirmbox.h"
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
|
||||||
QString cantInviteError() {
|
QString cantInviteError() {
|
||||||
|
@ -201,16 +202,20 @@ void ContactsInner::addAdminDone(const MTPUpdates &result, mtpRequestId req) {
|
||||||
|
|
||||||
_addAdminRequestId = 0;
|
_addAdminRequestId = 0;
|
||||||
if (_addAdmin && _channel && _channel->isMegagroup()) {
|
if (_addAdmin && _channel && _channel->isMegagroup()) {
|
||||||
|
Notify::PeerUpdate update(_channel);
|
||||||
if (_channel->mgInfo->lastParticipants.indexOf(_addAdmin) < 0) {
|
if (_channel->mgInfo->lastParticipants.indexOf(_addAdmin) < 0) {
|
||||||
_channel->mgInfo->lastParticipants.push_front(_addAdmin);
|
_channel->mgInfo->lastParticipants.push_front(_addAdmin);
|
||||||
|
update.flags |= Notify::PeerUpdate::Flag::MembersChanged;
|
||||||
}
|
}
|
||||||
_channel->mgInfo->lastAdmins.insert(_addAdmin);
|
_channel->mgInfo->lastAdmins.insert(_addAdmin);
|
||||||
|
update.flags |= Notify::PeerUpdate::Flag::AdminsChanged;
|
||||||
if (_addAdmin->botInfo) {
|
if (_addAdmin->botInfo) {
|
||||||
_channel->mgInfo->bots.insert(_addAdmin);
|
_channel->mgInfo->bots.insert(_addAdmin);
|
||||||
if (_channel->mgInfo->botStatus != 0 && _channel->mgInfo->botStatus < 2) {
|
if (_channel->mgInfo->botStatus != 0 && _channel->mgInfo->botStatus < 2) {
|
||||||
_channel->mgInfo->botStatus = 2;
|
_channel->mgInfo->botStatus = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
}
|
}
|
||||||
if (_addAdminBox) _addAdminBox->onClose();
|
if (_addAdminBox) _addAdminBox->onClose();
|
||||||
emit adminAdded();
|
emit adminAdded();
|
||||||
|
@ -224,7 +229,7 @@ bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) {
|
||||||
_addAdminRequestId = 0;
|
_addAdminRequestId = 0;
|
||||||
if (_addAdminBox) _addAdminBox->onClose();
|
if (_addAdminBox) _addAdminBox->onClose();
|
||||||
if (error.type() == "USERS_TOO_MUCH") {
|
if (error.type() == "USERS_TOO_MUCH") {
|
||||||
Ui::showLayer(new MaxInviteBox(_channel->invitationUrl), KeepOtherLayers);
|
Ui::showLayer(new MaxInviteBox(_channel->inviteLink()), KeepOtherLayers);
|
||||||
} else if (error.type() == "ADMINS_TOO_MUCH") {
|
} else if (error.type() == "ADMINS_TOO_MUCH") {
|
||||||
Ui::showLayer(new InformBox(lang(lng_channel_admins_too_much)), KeepOtherLayers);
|
Ui::showLayer(new InformBox(lang(lng_channel_admins_too_much)), KeepOtherLayers);
|
||||||
} else if (error.type() == qstr("USER_RESTRICTED")) {
|
} else if (error.type() == qstr("USER_RESTRICTED")) {
|
||||||
|
@ -728,7 +733,7 @@ void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) {
|
||||||
_checkedContacts.insert(peer, true);
|
_checkedContacts.insert(peer, true);
|
||||||
++_selCount;
|
++_selCount;
|
||||||
} else if (_channel && !_channel->isMegagroup()) {
|
} else if (_channel && !_channel->isMegagroup()) {
|
||||||
Ui::showLayer(new MaxInviteBox(_channel->invitationUrl), KeepOtherLayers);
|
Ui::showLayer(new MaxInviteBox(_channel->inviteLink()), KeepOtherLayers);
|
||||||
} else if (!_channel && selectedCount() >= Global::ChatSizeMax() && selectedCount() < Global::MegagroupSizeMax()) {
|
} else if (!_channel && selectedCount() >= Global::ChatSizeMax() && selectedCount() < Global::MegagroupSizeMax()) {
|
||||||
Ui::showLayer(new InformBox(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
|
Ui::showLayer(new InformBox(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
|
||||||
}
|
}
|
||||||
|
@ -740,7 +745,7 @@ int32 ContactsInner::selectedCount() const {
|
||||||
if (_chat) {
|
if (_chat) {
|
||||||
result += qMax(_chat->count, 1);
|
result += qMax(_chat->count, 1);
|
||||||
} else if (_channel) {
|
} else if (_channel) {
|
||||||
result += qMax(_channel->count, _already.size());
|
result += qMax(_channel->membersCount(), _already.size());
|
||||||
} else if (_creating == CreatingGroupGroup) {
|
} else if (_creating == CreatingGroupGroup) {
|
||||||
result += 1;
|
result += 1;
|
||||||
}
|
}
|
||||||
|
@ -1739,7 +1744,7 @@ bool ContactsBox::creationFail(const RPCError &error) {
|
||||||
|
|
||||||
MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : TWidget()
|
MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : TWidget()
|
||||||
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
|
||||||
, _newItemHeight((channel->amCreator() && (channel->count < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilterAdmins)) ? st::contactsNewItemHeight : 0)
|
, _newItemHeight((channel->amCreator() && (channel->membersCount() < (channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!channel->isMegagroup() && !channel->isPublic()) || filter == MembersFilterAdmins)) ? st::contactsNewItemHeight : 0)
|
||||||
, _newItemSel(false)
|
, _newItemSel(false)
|
||||||
, _channel(channel)
|
, _channel(channel)
|
||||||
, _filter(filter)
|
, _filter(filter)
|
||||||
|
@ -1808,7 +1813,7 @@ void MembersInner::paintEvent(QPaintEvent *e) {
|
||||||
paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown);
|
paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown);
|
||||||
p.translate(0, _rowHeight);
|
p.translate(0, _rowHeight);
|
||||||
}
|
}
|
||||||
if (to == _rows.size() && _filter == MembersFilterRecent && (_rows.size() < _channel->count || _rows.size() >= Global::ChatSizeMax())) {
|
if (to == _rows.size() && _filter == MembersFilterRecent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) {
|
||||||
p.setPen(st::stickersReorderFg);
|
p.setPen(st::stickersReorderFg);
|
||||||
_about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center);
|
_about.draw(p, st::contactsPadding.left(), st::stickersReorderPadding.top(), _aboutWidth, style::al_center);
|
||||||
}
|
}
|
||||||
|
@ -1976,7 +1981,7 @@ void MembersInner::chooseParticipant() {
|
||||||
if (_sel < 0 || _sel >= _rows.size()) return;
|
if (_sel < 0 || _sel >= _rows.size()) return;
|
||||||
if (PeerData *peer = _rows[_sel]) {
|
if (PeerData *peer = _rows[_sel]) {
|
||||||
Ui::hideLayer();
|
Ui::hideLayer();
|
||||||
App::main()->showPeerProfile(peer, ShowAtUnreadMsgId);
|
Ui::showPeerProfile(peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1987,7 +1992,7 @@ void MembersInner::refresh() {
|
||||||
} else {
|
} else {
|
||||||
_about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size()));
|
_about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size()));
|
||||||
_aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom();
|
_aboutHeight = st::stickersReorderPadding.top() + _about.countHeight(_aboutWidth) + st::stickersReorderPadding.bottom();
|
||||||
if (_filter != MembersFilterRecent || (_rows.size() >= _channel->count && _rows.size() < Global::ChatSizeMax())) {
|
if (_filter != MembersFilterRecent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) {
|
||||||
_aboutHeight = 0;
|
_aboutHeight = 0;
|
||||||
}
|
}
|
||||||
resize(width(), st::membersPadding.top() + _newItemHeight + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight);
|
resize(width(), st::membersPadding.top() + _newItemHeight + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight);
|
||||||
|
@ -2003,11 +2008,11 @@ MembersFilter MembersInner::filter() const {
|
||||||
return _filter;
|
return _filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<UserData*, bool> MembersInner::already() const {
|
MembersAlreadyIn MembersInner::already() const {
|
||||||
MembersAlreadyIn result;
|
MembersAlreadyIn result;
|
||||||
for (int32 i = 0, l = _rows.size(); i < l; ++i) {
|
for_const (auto peer, _rows) {
|
||||||
if (_rows.at(i)->isUser()) {
|
if (peer->isUser()) {
|
||||||
result.insert(_rows.at(i)->asUser(), true);
|
result.insert(peer->asUser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -2115,14 +2120,16 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
|
||||||
_datas.reserve(v.size());
|
_datas.reserve(v.size());
|
||||||
_dates.reserve(v.size());
|
_dates.reserve(v.size());
|
||||||
_roles.reserve(v.size());
|
_roles.reserve(v.size());
|
||||||
if (_filter == MembersFilterRecent && _channel->count < d.vcount.v) {
|
|
||||||
_channel->count = d.vcount.v;
|
if (_filter == MembersFilterRecent && _channel->membersCount() < d.vcount.v) {
|
||||||
|
_channel->setMembersCount(d.vcount.v);
|
||||||
if (App::main()) emit App::main()->peerUpdated(_channel);
|
if (App::main()) emit App::main()->peerUpdated(_channel);
|
||||||
} else if (_filter == MembersFilterAdmins && _channel->adminsCount < d.vcount.v) {
|
} else if (_filter == MembersFilterAdmins && _channel->adminsCount() < d.vcount.v) {
|
||||||
_channel->adminsCount = d.vcount.v;
|
_channel->setAdminsCount(d.vcount.v);
|
||||||
if (App::main()) emit App::main()->peerUpdated(_channel);
|
if (App::main()) emit App::main()->peerUpdated(_channel);
|
||||||
}
|
}
|
||||||
App::feedUsers(d.vusers);
|
App::feedUsers(d.vusers);
|
||||||
|
|
||||||
for (QVector<MTPChannelParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
for (QVector<MTPChannelParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
||||||
int32 userId = 0, addedTime = 0;
|
int32 userId = 0, addedTime = 0;
|
||||||
MemberRole role = MemberRoleNone;
|
MemberRole role = MemberRoleNone;
|
||||||
|
@ -2173,6 +2180,8 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
|
||||||
_channel->mgInfo->lastAdmins.insert(_rows.at(i));
|
_channel->mgInfo->lastAdmins.insert(_rows.at(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_rows.isEmpty()) {
|
if (_rows.isEmpty()) {
|
||||||
|
@ -2229,11 +2238,11 @@ void MembersInner::removeKicked() {
|
||||||
_dates.removeAt(index);
|
_dates.removeAt(index);
|
||||||
_roles.removeAt(index);
|
_roles.removeAt(index);
|
||||||
clearSel();
|
clearSel();
|
||||||
if (_filter == MembersFilterRecent && _channel->count > 1) {
|
if (_filter == MembersFilterRecent && _channel->membersCount() > 1) {
|
||||||
--_channel->count;
|
_channel->setMembersCount(_channel->membersCount() - 1);
|
||||||
if (App::main()) emit App::main()->peerUpdated(_channel);
|
if (App::main()) emit App::main()->peerUpdated(_channel);
|
||||||
} else if (_filter == MembersFilterAdmins && _channel->adminsCount > 1) {
|
} else if (_filter == MembersFilterAdmins && _channel->adminsCount() > 1) {
|
||||||
--_channel->adminsCount;
|
_channel->setAdminsCount(_channel->adminsCount() - 1);
|
||||||
if (App::main()) emit App::main()->peerUpdated(_channel);
|
if (App::main()) emit App::main()->peerUpdated(_channel);
|
||||||
}
|
}
|
||||||
refresh();
|
refresh();
|
||||||
|
@ -2290,8 +2299,8 @@ void MembersBox::onScroll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MembersBox::onAdd() {
|
void MembersBox::onAdd() {
|
||||||
if (_inner.filter() == MembersFilterRecent && _inner.channel()->count >= (_inner.channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) {
|
if (_inner.filter() == MembersFilterRecent && _inner.channel()->membersCount() >= (_inner.channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) {
|
||||||
Ui::showLayer(new MaxInviteBox(_inner.channel()->invitationUrl), KeepOtherLayers);
|
Ui::showLayer(new MaxInviteBox(_inner.channel()->inviteLink()), KeepOtherLayers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ContactsBox *box = new ContactsBox(_inner.channel(), _inner.filter(), _inner.already());
|
ContactsBox *box = new ContactsBox(_inner.channel(), _inner.filter(), _inner.already());
|
||||||
|
|
|
@ -31,7 +31,7 @@ enum MembersFilter {
|
||||||
MembersFilterRecent,
|
MembersFilterRecent,
|
||||||
MembersFilterAdmins,
|
MembersFilterAdmins,
|
||||||
};
|
};
|
||||||
typedef QMap<UserData*, bool> MembersAlreadyIn;
|
using MembersAlreadyIn = OrderedSet<UserData*>;
|
||||||
|
|
||||||
QString cantInviteError();
|
QString cantInviteError();
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ public:
|
||||||
}
|
}
|
||||||
void clearSel();
|
void clearSel();
|
||||||
|
|
||||||
QMap<UserData*, bool> already() const;
|
MembersAlreadyIn already() const;
|
||||||
|
|
||||||
~MembersInner();
|
~MembersInner();
|
||||||
|
|
||||||
|
|
147
Telegram/SourceFiles/boxes/report_box.cpp
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "report_box.h"
|
||||||
|
|
||||||
|
#include "lang.h"
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "boxes/confirmbox.h"
|
||||||
|
|
||||||
|
ReportBox::ReportBox(ChannelData *channel) : AbstractBox(st::boxWidth)
|
||||||
|
, _channel(channel)
|
||||||
|
, _reasonSpam(this, qsl("report_reason"), ReasonSpam, lang(lng_report_reason_spam), true)
|
||||||
|
, _reasonViolence(this, qsl("report_reason"), ReasonViolence, lang(lng_report_reason_violence))
|
||||||
|
, _reasonPornography(this, qsl("report_reason"), ReasonPornography, lang(lng_report_reason_pornography))
|
||||||
|
, _reasonOther(this, qsl("report_reason"), ReasonOther, lang(lng_report_reason_other))
|
||||||
|
, _report(this, lang(lng_report_button), st::defaultBoxButton)
|
||||||
|
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
|
||||||
|
connect(_report, SIGNAL(clicked()), this, SLOT(onReport()));
|
||||||
|
connect(_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
|
||||||
|
|
||||||
|
connect(_reasonSpam, SIGNAL(changed()), this, SLOT(onChange()));
|
||||||
|
connect(_reasonViolence, SIGNAL(changed()), this, SLOT(onChange()));
|
||||||
|
connect(_reasonPornography, SIGNAL(changed()), this, SLOT(onChange()));
|
||||||
|
connect(_reasonOther, SIGNAL(changed()), this, SLOT(onChange()));
|
||||||
|
|
||||||
|
updateMaxHeight();
|
||||||
|
|
||||||
|
prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
if (paint(p)) return;
|
||||||
|
|
||||||
|
paintTitle(p, lang(lng_report_title));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::resizeEvent(QResizeEvent *e) {
|
||||||
|
_reasonSpam->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), st::boxTitleHeight + st::boxOptionListPadding.top());
|
||||||
|
_reasonViolence->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonSpam->y() + _reasonSpam->height() + st::boxOptionListPadding.top());
|
||||||
|
_reasonPornography->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonViolence->y() + _reasonViolence->height() + st::boxOptionListPadding.top());
|
||||||
|
_reasonOther->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), _reasonPornography->y() + _reasonPornography->height() + st::boxOptionListPadding.top());
|
||||||
|
|
||||||
|
if (_reasonOtherText) {
|
||||||
|
_reasonOtherText->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left() - st::defaultInputField.textMargins.left(), _reasonOther->y() + _reasonOther->height() + st::newGroupDescriptionPadding.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
_report->moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _report->height());
|
||||||
|
_cancel->moveToRight(st::boxButtonPadding.right() + _report->width() + st::boxButtonPadding.left(), _report->y());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::onChange() {
|
||||||
|
if (_reasonOther->checked()) {
|
||||||
|
if (!_reasonOtherText) {
|
||||||
|
_reasonOtherText = new InputArea(this, st::profileReportReasonOther, lang(lng_report_reason_description));
|
||||||
|
_reasonOtherText->show();
|
||||||
|
_reasonOtherText->setCtrlEnterSubmit(CtrlEnterSubmitBoth);
|
||||||
|
_reasonOtherText->setMaxLength(MaxPhotoCaption);
|
||||||
|
_reasonOtherText->resize(width() - (st::boxPadding.left() + st::boxOptionListPadding.left() + st::boxPadding.right()), _reasonOtherText->height());
|
||||||
|
|
||||||
|
updateMaxHeight();
|
||||||
|
connect(_reasonOtherText, SIGNAL(resized()), this, SLOT(onDescriptionResized()));
|
||||||
|
connect(_reasonOtherText, SIGNAL(submitted(bool)), this, SLOT(onReport()));
|
||||||
|
connect(_reasonOtherText, SIGNAL(cancelled()), this, SLOT(onClose()));
|
||||||
|
}
|
||||||
|
} else if (_reasonOtherText) {
|
||||||
|
_reasonOtherText.destroy();
|
||||||
|
updateMaxHeight();
|
||||||
|
}
|
||||||
|
setInnerFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::setInnerFocus() {
|
||||||
|
if (_reasonOtherText) {
|
||||||
|
_reasonOtherText->setFocus();
|
||||||
|
} else {
|
||||||
|
setFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::onDescriptionResized() {
|
||||||
|
updateMaxHeight();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::onReport() {
|
||||||
|
if (_requestId) return;
|
||||||
|
|
||||||
|
if (_reasonOtherText && _reasonOtherText->getLastText().trimmed().isEmpty()) {
|
||||||
|
_reasonOtherText->showError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getReason = [this]() {
|
||||||
|
if (_reasonViolence->checked()) {
|
||||||
|
return MTP_inputReportReasonViolence();
|
||||||
|
} else if (_reasonPornography->checked()) {
|
||||||
|
return MTP_inputReportReasonPornography();
|
||||||
|
} else if (_reasonOtherText) {
|
||||||
|
return MTP_inputReportReasonOther(MTP_string(_reasonOtherText->getLastText()));
|
||||||
|
} else {
|
||||||
|
return MTP_inputReportReasonSpam();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_requestId = MTP::send(MTPaccount_ReportPeer(_channel->input, getReason()), rpcDone(&ReportBox::reportDone), rpcFail(&ReportBox::reportFail));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::reportDone(const MTPBool &result) {
|
||||||
|
_requestId = 0;
|
||||||
|
Ui::showLayer(new InformBox(lang(lng_report_thanks)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReportBox::reportFail(const RPCError &error) {
|
||||||
|
if (MTP::isDefaultHandledError(error)) return false;
|
||||||
|
|
||||||
|
_requestId = 0;
|
||||||
|
if (_reasonOtherText) {
|
||||||
|
_reasonOtherText->showError();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportBox::updateMaxHeight() {
|
||||||
|
int32 h = st::boxTitleHeight + 4 * (st::boxOptionListPadding.top() + _reasonSpam->height()) + st::boxButtonPadding.top() + _report->height() + st::boxButtonPadding.bottom();
|
||||||
|
if (_reasonOtherText) {
|
||||||
|
h += st::newGroupDescriptionPadding.top() + _reasonOtherText->height() + st::newGroupDescriptionPadding.bottom();
|
||||||
|
}
|
||||||
|
setMaxHeight(h);
|
||||||
|
}
|
72
Telegram/SourceFiles/boxes/report_box.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "abstractbox.h"
|
||||||
|
|
||||||
|
class ReportBox : public AbstractBox, public RPCSender {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ReportBox(ChannelData *channel);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onReport();
|
||||||
|
void onChange();
|
||||||
|
void onDescriptionResized();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
|
void showAll() override {
|
||||||
|
showChildren();
|
||||||
|
}
|
||||||
|
void hideAll() override {
|
||||||
|
hideChildren();
|
||||||
|
}
|
||||||
|
void setInnerFocus() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateMaxHeight();
|
||||||
|
|
||||||
|
void reportDone(const MTPBool &result);
|
||||||
|
bool reportFail(const RPCError &error);
|
||||||
|
|
||||||
|
ChannelData *_channel;
|
||||||
|
|
||||||
|
ChildWidget<Radiobutton> _reasonSpam;
|
||||||
|
ChildWidget<Radiobutton> _reasonViolence;
|
||||||
|
ChildWidget<Radiobutton> _reasonPornography;
|
||||||
|
ChildWidget<Radiobutton> _reasonOther;
|
||||||
|
ChildWidget<InputArea> _reasonOtherText = { nullptr };
|
||||||
|
|
||||||
|
ChildWidget<BoxButton> _report, _cancel;
|
||||||
|
|
||||||
|
enum Reason {
|
||||||
|
ReasonSpam,
|
||||||
|
ReasonViolence,
|
||||||
|
ReasonPornography,
|
||||||
|
ReasonOther,
|
||||||
|
};
|
||||||
|
mtpRequestId _requestId = 0;
|
||||||
|
|
||||||
|
};
|
|
@ -289,7 +289,7 @@ QString Generator::valueAssignmentCode(structure::Value value) const {
|
||||||
} break;
|
} break;
|
||||||
case Tag::Icon: {
|
case Tag::Icon: {
|
||||||
auto v(value.Icon());
|
auto v(value.Icon());
|
||||||
if (v.parts.empty()) return QString();
|
if (v.parts.empty()) return QString("{}");
|
||||||
|
|
||||||
QStringList parts;
|
QStringList parts;
|
||||||
for (const auto &part : v.parts) {
|
for (const auto &part : v.parts) {
|
||||||
|
|
|
@ -535,8 +535,8 @@ structure::Value ParsedFile::readPointValue() {
|
||||||
if (tokenValue(font) == "point") {
|
if (tokenValue(font) == "point") {
|
||||||
assertNextToken(BasicType::LeftParenthesis);
|
assertNextToken(BasicType::LeftParenthesis);
|
||||||
|
|
||||||
auto x = readNumericValue(); assertNextToken(BasicType::Comma);
|
auto x = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
|
||||||
auto y = readNumericValue();
|
auto y = readNumericOrNumericCopyValue();
|
||||||
if (x.type().tag != structure::TypeTag::Pixels ||
|
if (x.type().tag != structure::TypeTag::Pixels ||
|
||||||
y.type().tag != structure::TypeTag::Pixels) {
|
y.type().tag != structure::TypeTag::Pixels) {
|
||||||
logErrorTypeMismatch() << "expected two px values for the point";
|
logErrorTypeMismatch() << "expected two px values for the point";
|
||||||
|
@ -581,8 +581,8 @@ structure::Value ParsedFile::readSizeValue() {
|
||||||
if (tokenValue(font) == "size") {
|
if (tokenValue(font) == "size") {
|
||||||
assertNextToken(BasicType::LeftParenthesis);
|
assertNextToken(BasicType::LeftParenthesis);
|
||||||
|
|
||||||
auto w = readNumericValue(); assertNextToken(BasicType::Comma);
|
auto w = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
|
||||||
auto h = readNumericValue();
|
auto h = readNumericOrNumericCopyValue();
|
||||||
if (w.type().tag != structure::TypeTag::Pixels ||
|
if (w.type().tag != structure::TypeTag::Pixels ||
|
||||||
h.type().tag != structure::TypeTag::Pixels) {
|
h.type().tag != structure::TypeTag::Pixels) {
|
||||||
logErrorTypeMismatch() << "expected two px values for the size";
|
logErrorTypeMismatch() << "expected two px values for the size";
|
||||||
|
@ -662,10 +662,10 @@ structure::Value ParsedFile::readMarginsValue() {
|
||||||
if (tokenValue(font) == "margins") {
|
if (tokenValue(font) == "margins") {
|
||||||
assertNextToken(BasicType::LeftParenthesis);
|
assertNextToken(BasicType::LeftParenthesis);
|
||||||
|
|
||||||
auto l = readNumericValue(); assertNextToken(BasicType::Comma);
|
auto l = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
|
||||||
auto t = readNumericValue(); assertNextToken(BasicType::Comma);
|
auto t = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
|
||||||
auto r = readNumericValue(); assertNextToken(BasicType::Comma);
|
auto r = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
|
||||||
auto b = readNumericValue();
|
auto b = readNumericOrNumericCopyValue();
|
||||||
if (l.type().tag != structure::TypeTag::Pixels ||
|
if (l.type().tag != structure::TypeTag::Pixels ||
|
||||||
t.type().tag != structure::TypeTag::Pixels ||
|
t.type().tag != structure::TypeTag::Pixels ||
|
||||||
r.type().tag != structure::TypeTag::Pixels ||
|
r.type().tag != structure::TypeTag::Pixels ||
|
||||||
|
@ -701,18 +701,10 @@ structure::Value ParsedFile::readFontValue() {
|
||||||
file_.putBack();
|
file_.putBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto familyValue = readStringValue()) {
|
if (auto familyValue = readStringOrStringCopyValue()) {
|
||||||
family = familyValue;
|
family = familyValue;
|
||||||
} else if (auto sizeValue = readNumericValue()) {
|
} else if (auto sizeValue = readNumericOrNumericCopyValue()) {
|
||||||
size = sizeValue;
|
size = sizeValue;
|
||||||
} else if (auto copyValue = readCopyValue()) {
|
|
||||||
if (copyValue.type().tag == structure::TypeTag::String) {
|
|
||||||
family = copyValue;
|
|
||||||
} else if (copyValue.type().tag == structure::TypeTag::Pixels) {
|
|
||||||
size = copyValue;
|
|
||||||
} else {
|
|
||||||
logErrorUnexpectedToken() << "font family, font size or ')'";
|
|
||||||
}
|
|
||||||
} else if (file_.getToken(BasicType::RightParenthesis)) {
|
} else if (file_.getToken(BasicType::RightParenthesis)) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -776,7 +768,37 @@ structure::Value ParsedFile::readCopyValue() {
|
||||||
if (auto variable = module_->findVariable(name)) {
|
if (auto variable = module_->findVariable(name)) {
|
||||||
return variable->value.makeCopy(variable->name);
|
return variable->value.makeCopy(variable->name);
|
||||||
}
|
}
|
||||||
logError(kErrorIdentifierNotFound) << "identifier '" << logFullName(name) << "' not found";
|
file_.putBack();
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
structure::Value ParsedFile::readNumericOrNumericCopyValue() {
|
||||||
|
if (auto result = readNumericValue()) {
|
||||||
|
return result;
|
||||||
|
} else if (auto copy = readCopyValue()) {
|
||||||
|
auto type = copy.type().tag;
|
||||||
|
if (type == structure::TypeTag::Int
|
||||||
|
|| type == structure::TypeTag::Double
|
||||||
|
|| type == structure::TypeTag::Pixels) {
|
||||||
|
return copy;
|
||||||
|
} else {
|
||||||
|
file_.putBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
structure::Value ParsedFile::readStringOrStringCopyValue() {
|
||||||
|
if (auto result = readStringValue()) {
|
||||||
|
return result;
|
||||||
|
} else if (auto copy = readCopyValue()) {
|
||||||
|
auto type = copy.type().tag;
|
||||||
|
if (type == structure::TypeTag::String) {
|
||||||
|
return copy;
|
||||||
|
} else {
|
||||||
|
file_.putBack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,9 @@ private:
|
||||||
structure::Value readIconValue();
|
structure::Value readIconValue();
|
||||||
structure::Value readCopyValue();
|
structure::Value readCopyValue();
|
||||||
|
|
||||||
|
structure::Value readNumericOrNumericCopyValue();
|
||||||
|
structure::Value readStringOrStringCopyValue();
|
||||||
|
|
||||||
structure::data::monoicon readMonoIconFields();
|
structure::data::monoicon readMonoIconFields();
|
||||||
QString readMonoIconFilename();
|
QString readMonoIconFilename();
|
||||||
|
|
||||||
|
@ -131,6 +134,7 @@ private:
|
||||||
{ "align" , { structure::TypeTag::Align } },
|
{ "align" , { structure::TypeTag::Align } },
|
||||||
{ "margins" , { structure::TypeTag::Margins } },
|
{ "margins" , { structure::TypeTag::Margins } },
|
||||||
{ "font" , { structure::TypeTag::Font } },
|
{ "font" , { structure::TypeTag::Font } },
|
||||||
|
{ "icon" , { structure::TypeTag::Icon } },
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -74,14 +74,15 @@ bool Processor::write(const structure::Module &module) const {
|
||||||
QFileInfo srcFile(module.filepath());
|
QFileInfo srcFile(module.filepath());
|
||||||
QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module);
|
QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module);
|
||||||
|
|
||||||
|
bool forceReGenerate = false;// !options_.rebuildDependencies;
|
||||||
common::ProjectInfo project = {
|
common::ProjectInfo project = {
|
||||||
"codegen_style",
|
"codegen_style",
|
||||||
srcFile.fileName(),
|
srcFile.fileName(),
|
||||||
"stdafx.h",
|
"stdafx.h",
|
||||||
false,//!options_.rebuildDependencies, // forceReGenerate
|
forceReGenerate
|
||||||
};
|
};
|
||||||
|
|
||||||
SpriteGenerator spriteGenerator(module);
|
SpriteGenerator spriteGenerator(module, forceReGenerate);
|
||||||
if (!spriteGenerator.writeSprites()) {
|
if (!spriteGenerator.writeSprites()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,9 @@ constexpr int kErrorCouldNotWrite = 845;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
SpriteGenerator::SpriteGenerator(const structure::Module &module)
|
SpriteGenerator::SpriteGenerator(const structure::Module &module, bool forceReGenerate)
|
||||||
: module_(module)
|
: module_(module)
|
||||||
|
, forceReGenerate_(forceReGenerate)
|
||||||
, basePath_(QFileInfo(module.filepath()).dir().absolutePath()) {
|
, basePath_(QFileInfo(module.filepath()).dir().absolutePath()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +85,7 @@ bool SpriteGenerator::writeSprites() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QFile file(filepath);
|
QFile file(filepath);
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
if (!forceReGenerate_ && file.open(QIODevice::ReadOnly)) {
|
||||||
if (file.readAll() == spriteData) {
|
if (file.readAll() == spriteData) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ class Module;
|
||||||
|
|
||||||
class SpriteGenerator {
|
class SpriteGenerator {
|
||||||
public:
|
public:
|
||||||
SpriteGenerator(const structure::Module &module);
|
SpriteGenerator(const structure::Module &module, bool forceReGenerate);
|
||||||
SpriteGenerator(const SpriteGenerator &other) = delete;
|
SpriteGenerator(const SpriteGenerator &other) = delete;
|
||||||
SpriteGenerator &operator=(const SpriteGenerator &other) = delete;
|
SpriteGenerator &operator=(const SpriteGenerator &other) = delete;
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ private:
|
||||||
QImage generateSprite(int scale); // scale = 5 for 125% and 6 for 150%.
|
QImage generateSprite(int scale); // scale = 5 for 125% and 6 for 150%.
|
||||||
|
|
||||||
const structure::Module &module_;
|
const structure::Module &module_;
|
||||||
|
bool forceReGenerate_;
|
||||||
QString basePath_;
|
QString basePath_;
|
||||||
QImage sprite2x_;
|
QImage sprite2x_;
|
||||||
QList<structure::Variable> sprites_;
|
QList<structure::Variable> sprites_;
|
||||||
|
|
|
@ -44,7 +44,6 @@ uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 };
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
|
|
||||||
// Base types compile-time check
|
// Base types compile-time check
|
||||||
|
|
||||||
static_assert(sizeof(char) == 1, "Basic types size check failed");
|
static_assert(sizeof(char) == 1, "Basic types size check failed");
|
||||||
static_assert(sizeof(uchar) == 1, "Basic types size check failed");
|
static_assert(sizeof(uchar) == 1, "Basic types size check failed");
|
||||||
static_assert(sizeof(int16) == 2, "Basic types size check failed");
|
static_assert(sizeof(int16) == 2, "Basic types size check failed");
|
||||||
|
|
|
@ -33,6 +33,13 @@ T *getPointerAndReset(T *&ptr) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T createAndSwap(T &value) {
|
||||||
|
T result;
|
||||||
|
std::swap(result, value);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
struct NullType {
|
struct NullType {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -421,6 +428,24 @@ inline bool operator!=(std::nullptr_t a, const unique_ptr<T> &b) noexcept {
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using _yes = char(&)[1];
|
||||||
|
using _no = char(&)[2];
|
||||||
|
|
||||||
|
template <typename Base, typename Derived>
|
||||||
|
struct _host {
|
||||||
|
operator Base*() const;
|
||||||
|
operator Derived*();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Base, typename Derived>
|
||||||
|
struct is_base_of {
|
||||||
|
template <typename T>
|
||||||
|
static _yes check(Derived*, T);
|
||||||
|
static _no check(Base*, int);
|
||||||
|
|
||||||
|
static constexpr bool value = sizeof(check(_host<Base, Derived>(), int())) == sizeof(_yes);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace std_
|
} // namespace std_
|
||||||
|
|
||||||
#include "logs.h"
|
#include "logs.h"
|
||||||
|
@ -927,15 +952,6 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename I>
|
|
||||||
inline void destroyImplementation(I *&ptr) {
|
|
||||||
if (ptr) {
|
|
||||||
ptr->destroy();
|
|
||||||
ptr = 0;
|
|
||||||
}
|
|
||||||
deleteAndMark(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Composer;
|
class Composer;
|
||||||
typedef void(*ComponentConstruct)(void *location, Composer *composer);
|
typedef void(*ComponentConstruct)(void *location, Composer *composer);
|
||||||
typedef void(*ComponentDestruct)(void *location);
|
typedef void(*ComponentDestruct)(void *location);
|
||||||
|
@ -1170,213 +1186,96 @@ public:
|
||||||
virtual R call(Args... args) const = 0;
|
virtual R call(Args... args) const = 0;
|
||||||
virtual ~SharedCallback() {
|
virtual ~SharedCallback() {
|
||||||
}
|
}
|
||||||
typedef QSharedPointer<SharedCallback<R, Args...>> Ptr;
|
using Ptr = QSharedPointer<SharedCallback<R, Args...>>;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename R>
|
template <typename R, typename... Args>
|
||||||
class FunctionImplementation {
|
class FunctionImplementation {
|
||||||
public:
|
public:
|
||||||
virtual R call() = 0;
|
virtual R call(Args... args) = 0;
|
||||||
virtual void destroy() { delete this; }
|
virtual void destroy() { delete this; }
|
||||||
virtual ~FunctionImplementation() {}
|
virtual ~FunctionImplementation() {}
|
||||||
|
|
||||||
};
|
};
|
||||||
template <typename R>
|
|
||||||
class NullFunctionImplementation : public FunctionImplementation<R> {
|
template <typename R, typename... Args>
|
||||||
|
class NullFunctionImplementation : public FunctionImplementation<R, Args...> {
|
||||||
public:
|
public:
|
||||||
virtual R call() { return R(); }
|
virtual R call(Args... args) { return R(); }
|
||||||
virtual void destroy() {}
|
virtual void destroy() {}
|
||||||
static NullFunctionImplementation<R> SharedInstance;
|
static NullFunctionImplementation<R, Args...> SharedInstance;
|
||||||
|
|
||||||
};
|
};
|
||||||
template <typename R>
|
template <typename R, typename... Args>
|
||||||
NullFunctionImplementation<R> NullFunctionImplementation<R>::SharedInstance;
|
NullFunctionImplementation<R, Args...> NullFunctionImplementation<R, Args...>::SharedInstance;
|
||||||
template <typename R>
|
|
||||||
class FunctionCreator {
|
template <typename R, typename... Args>
|
||||||
public:
|
|
||||||
FunctionCreator(FunctionImplementation<R> *ptr) : _ptr(ptr) {}
|
|
||||||
FunctionCreator(const FunctionCreator<R> &other) : _ptr(other.create()) {}
|
|
||||||
FunctionImplementation<R> *create() const { return getPointerAndReset(_ptr); }
|
|
||||||
~FunctionCreator() { destroyImplementation(_ptr); }
|
|
||||||
private:
|
|
||||||
FunctionCreator<R> &operator=(const FunctionCreator<R> &other);
|
|
||||||
mutable FunctionImplementation<R> *_ptr;
|
|
||||||
};
|
|
||||||
template <typename R>
|
|
||||||
class Function {
|
class Function {
|
||||||
public:
|
public:
|
||||||
typedef FunctionCreator<R> Creator;
|
Function() : _implementation(nullImpl()) {}
|
||||||
static Creator Null() { return Creator(&NullFunctionImplementation<R>::SharedInstance); }
|
Function(FunctionImplementation<R, Args...> *implementation) : _implementation(implementation) {}
|
||||||
Function(const Creator &creator) : _implementation(creator.create()) {}
|
Function(const Function<R, Args...> &other) = delete;
|
||||||
R call() { return _implementation->call(); }
|
Function<R, Args...> &operator=(const Function<R, Args...> &other) = delete;
|
||||||
~Function() { destroyImplementation(_implementation); }
|
Function(Function<R, Args...> &&other) : _implementation(other._implementation) {
|
||||||
|
other._implementation = nullImpl();
|
||||||
|
}
|
||||||
|
Function<R, Args...> &operator=(Function<R, Args...> &&other) {
|
||||||
|
std::swap(_implementation, other._implementation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNull() const {
|
||||||
|
return (_implementation == nullImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
R call(Args... args) { return _implementation->call(args...); }
|
||||||
|
~Function() {
|
||||||
|
if (_implementation) {
|
||||||
|
_implementation->destroy();
|
||||||
|
_implementation = nullptr;
|
||||||
|
}
|
||||||
|
deleteAndMark(_implementation);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Function(const Function<R> &other);
|
static FunctionImplementation<R, Args...> *nullImpl() {
|
||||||
Function<R> &operator=(const Function<R> &other);
|
return &NullFunctionImplementation<R, Args...>::SharedInstance;
|
||||||
FunctionImplementation<R> *_implementation;
|
}
|
||||||
|
|
||||||
|
FunctionImplementation<R, Args...> *_implementation;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename R>
|
template <typename R, typename... Args>
|
||||||
class WrappedFunction : public FunctionImplementation<R> {
|
class WrappedFunction : public FunctionImplementation<R, Args...> {
|
||||||
public:
|
public:
|
||||||
typedef R(*Method)();
|
using Method = R(*)(Args... args);
|
||||||
WrappedFunction(Method method) : _method(method) {}
|
WrappedFunction(Method method) : _method(method) {}
|
||||||
virtual R call() { return (*_method)(); }
|
virtual R call(Args... args) { return (*_method)(args...); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Method _method;
|
Method _method;
|
||||||
|
|
||||||
};
|
};
|
||||||
template <typename R>
|
template <typename R, typename... Args>
|
||||||
inline FunctionCreator<R> func(R(*method)()) {
|
inline Function<R, Args...> func(R(*method)(Args... args)) {
|
||||||
return FunctionCreator<R>(new WrappedFunction<R>(method));
|
return Function<R, Args...>(new WrappedFunction<R, Args...>(method));
|
||||||
}
|
}
|
||||||
template <typename O, typename I, typename R>
|
|
||||||
class ObjectFunction : public FunctionImplementation<R> {
|
template <typename O, typename I, typename R, typename... Args>
|
||||||
|
class ObjectFunction : public FunctionImplementation<R, Args...> {
|
||||||
public:
|
public:
|
||||||
typedef R(I::*Method)();
|
using Method = R(I::*)(Args... args);
|
||||||
ObjectFunction(O *obj, Method method) : _obj(obj), _method(method) {}
|
ObjectFunction(O *obj, Method method) : _obj(obj), _method(method) {}
|
||||||
virtual R call() { return (_obj->*_method)(); }
|
virtual R call(Args... args) { return (_obj->*_method)(args...); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
O *_obj;
|
O *_obj;
|
||||||
Method _method;
|
Method _method;
|
||||||
};
|
|
||||||
template <typename O, typename I, typename R>
|
|
||||||
inline FunctionCreator<R> func(O *obj, R(I::*method)()) {
|
|
||||||
return FunctionCreator<R>(new ObjectFunction<O, I, R>(obj, method));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename R, typename A1>
|
|
||||||
class Function1Implementation {
|
|
||||||
public:
|
|
||||||
virtual R call(A1 a1) = 0;
|
|
||||||
virtual void destroy() { delete this; }
|
|
||||||
virtual ~Function1Implementation() {}
|
|
||||||
};
|
};
|
||||||
template <typename R, typename A1>
|
template <typename O, typename I, typename R, typename... Args>
|
||||||
class NullFunction1Implementation : public Function1Implementation<R, A1> {
|
inline Function<R, Args...> func(O *obj, R(I::*method)(Args...)) {
|
||||||
public:
|
return Function<R, Args...>(new ObjectFunction<O, I, R, Args...>(obj, method));
|
||||||
virtual R call(A1 a1) { return R(); }
|
|
||||||
virtual void destroy() {}
|
|
||||||
static NullFunction1Implementation<R, A1> SharedInstance;
|
|
||||||
};
|
|
||||||
template <typename R, typename A1>
|
|
||||||
NullFunction1Implementation<R, A1> NullFunction1Implementation<R, A1>::SharedInstance;
|
|
||||||
template <typename R, typename A1>
|
|
||||||
class Function1Creator {
|
|
||||||
public:
|
|
||||||
Function1Creator(Function1Implementation<R, A1> *ptr) : _ptr(ptr) {}
|
|
||||||
Function1Creator(const Function1Creator<R, A1> &other) : _ptr(other.create()) {}
|
|
||||||
Function1Implementation<R, A1> *create() const { return getPointerAndReset(_ptr); }
|
|
||||||
~Function1Creator() { destroyImplementation(_ptr); }
|
|
||||||
private:
|
|
||||||
Function1Creator<R, A1> &operator=(const Function1Creator<R, A1> &other);
|
|
||||||
mutable Function1Implementation<R, A1> *_ptr;
|
|
||||||
};
|
|
||||||
template <typename R, typename A1>
|
|
||||||
class Function1 {
|
|
||||||
public:
|
|
||||||
typedef Function1Creator<R, A1> Creator;
|
|
||||||
static Creator Null() { return Creator(&NullFunction1Implementation<R, A1>::SharedInstance); }
|
|
||||||
Function1(const Creator &creator) : _implementation(creator.create()) {}
|
|
||||||
R call(A1 a1) { return _implementation->call(a1); }
|
|
||||||
~Function1() { _implementation->destroy(); }
|
|
||||||
private:
|
|
||||||
Function1(const Function1<R, A1> &other);
|
|
||||||
Function1<R, A1> &operator=(const Function1<R, A1> &other);
|
|
||||||
Function1Implementation<R, A1> *_implementation;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename R, typename A1>
|
|
||||||
class WrappedFunction1 : public Function1Implementation<R, A1> {
|
|
||||||
public:
|
|
||||||
typedef R(*Method)(A1);
|
|
||||||
WrappedFunction1(Method method) : _method(method) {}
|
|
||||||
virtual R call(A1 a1) { return (*_method)(a1); }
|
|
||||||
private:
|
|
||||||
Method _method;
|
|
||||||
};
|
|
||||||
template <typename R, typename A1>
|
|
||||||
inline Function1Creator<R, A1> func(R(*method)(A1)) {
|
|
||||||
return Function1Creator<R, A1>(new WrappedFunction1<R, A1>(method));
|
|
||||||
}
|
|
||||||
template <typename O, typename I, typename R, typename A1>
|
|
||||||
class ObjectFunction1 : public Function1Implementation<R, A1> {
|
|
||||||
public:
|
|
||||||
typedef R(I::*Method)(A1);
|
|
||||||
ObjectFunction1(O *obj, Method method) : _obj(obj), _method(method) {}
|
|
||||||
virtual R call(A1 a1) { return (_obj->*_method)(a1); }
|
|
||||||
private:
|
|
||||||
O *_obj;
|
|
||||||
Method _method;
|
|
||||||
};
|
|
||||||
template <typename O, typename I, typename R, typename A1>
|
|
||||||
Function1Creator<R, A1> func(O *obj, R(I::*method)(A1)) {
|
|
||||||
return Function1Creator<R, A1>(new ObjectFunction1<O, I, R, A1>(obj, method));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
class Function2Implementation {
|
|
||||||
public:
|
|
||||||
virtual R call(A1 a1, A2 a2) = 0;
|
|
||||||
virtual void destroy() { delete this; }
|
|
||||||
virtual ~Function2Implementation() {}
|
|
||||||
};
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
class NullFunction2Implementation : public Function2Implementation<R, A1, A2> {
|
|
||||||
public:
|
|
||||||
virtual R call(A1 a1, A2 a2) { return R(); }
|
|
||||||
virtual void destroy() {}
|
|
||||||
static NullFunction2Implementation<R, A1, A2> SharedInstance;
|
|
||||||
};
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
NullFunction2Implementation<R, A1, A2> NullFunction2Implementation<R, A1, A2>::SharedInstance;
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
class Function2Creator {
|
|
||||||
public:
|
|
||||||
Function2Creator(Function2Implementation<R, A1, A2> *ptr) : _ptr(ptr) {}
|
|
||||||
Function2Creator(const Function2Creator<R, A1, A2> &other) : _ptr(other.create()) {}
|
|
||||||
Function2Implementation<R, A1, A2> *create() const { return getPointerAndReset(_ptr); }
|
|
||||||
~Function2Creator() { destroyImplementation(_ptr); }
|
|
||||||
private:
|
|
||||||
Function2Creator<R, A1, A2> &operator=(const Function2Creator<R, A1, A2> &other);
|
|
||||||
mutable Function2Implementation<R, A1, A2> *_ptr;
|
|
||||||
};
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
class Function2 {
|
|
||||||
public:
|
|
||||||
typedef Function2Creator<R, A1, A2> Creator;
|
|
||||||
static Creator Null() { return Creator(&NullFunction2Implementation<R, A1, A2>::SharedInstance); }
|
|
||||||
Function2(const Creator &creator) : _implementation(creator.create()) {}
|
|
||||||
R call(A1 a1, A2 a2) { return _implementation->call(a1, a2); }
|
|
||||||
~Function2() { destroyImplementation(_implementation); }
|
|
||||||
private:
|
|
||||||
Function2(const Function2<R, A1, A2> &other);
|
|
||||||
Function2<R, A1, A2> &operator=(const Function2<R, A1, A2> &other);
|
|
||||||
Function2Implementation<R, A1, A2> *_implementation;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
class WrappedFunction2 : public Function2Implementation<R, A1, A2> {
|
|
||||||
public:
|
|
||||||
typedef R(*Method)(A1, A2);
|
|
||||||
WrappedFunction2(Method method) : _method(method) {}
|
|
||||||
virtual R call(A1 a1, A2 a2) { return (*_method)(a1, a2); }
|
|
||||||
private:
|
|
||||||
Method _method;
|
|
||||||
};
|
|
||||||
template <typename R, typename A1, typename A2>
|
|
||||||
Function2Creator<R, A1, A2> func(R(*method)(A1, A2)) {
|
|
||||||
return Function2Creator<R, A1, A2>(new WrappedFunction2<R, A1, A2>(method));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename O, typename I, typename R, typename A1, typename A2>
|
|
||||||
class ObjectFunction2 : public Function2Implementation<R, A1, A2> {
|
|
||||||
public:
|
|
||||||
typedef R(I::*Method)(A1, A2);
|
|
||||||
ObjectFunction2(O *obj, Method method) : _obj(obj), _method(method) {}
|
|
||||||
virtual R call(A1 a1, A2 a2) { return (_obj->*_method)(a1, a2); }
|
|
||||||
private:
|
|
||||||
O *_obj;
|
|
||||||
Method _method;
|
|
||||||
};
|
|
||||||
template <typename O, typename I, typename R, typename A1, typename A2>
|
|
||||||
Function2Creator<R, A1, A2> func(O *obj, R(I::*method)(A1, A2)) {
|
|
||||||
return Function2Creator<R, A1, A2>(new ObjectFunction2<O, I, R, A1, A2>(obj, method));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum ExpandLinksMode {
|
||||||
ExpandLinksNone,
|
ExpandLinksNone,
|
||||||
ExpandLinksShortened,
|
ExpandLinksShortened,
|
||||||
ExpandLinksAll,
|
ExpandLinksAll,
|
||||||
|
ExpandLinksUrlOnly, // For custom urls leaves only url instead of text.
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClickHandlerHost {
|
class ClickHandlerHost {
|
||||||
|
@ -144,9 +145,11 @@ public:
|
||||||
}
|
}
|
||||||
static void hostDestroyed(ClickHandlerHost *host) {
|
static void hostDestroyed(ClickHandlerHost *host) {
|
||||||
if (_activeHost == host) {
|
if (_activeHost == host) {
|
||||||
|
if (_active) (*_active).clear();
|
||||||
_activeHost = nullptr;
|
_activeHost = nullptr;
|
||||||
}
|
}
|
||||||
if (_pressedHost == host) {
|
if (_pressedHost == host) {
|
||||||
|
if (_pressed) (*_pressed).clear();
|
||||||
_pressedHost = nullptr;
|
_pressedHost = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,15 +110,22 @@ QString HiddenUrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const Q
|
||||||
QString result;
|
QString result;
|
||||||
if (mode == ExpandLinksAll) {
|
if (mode == ExpandLinksAll) {
|
||||||
result = textPart.toString() + qsl(" (") + url() + ')';
|
result = textPart.toString() + qsl(" (") + url() + ')';
|
||||||
|
} else if (mode == ExpandLinksUrlOnly) {
|
||||||
|
result = url();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
TextWithEntities HiddenUrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
TextWithEntities HiddenUrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
|
||||||
TextWithEntities result;
|
TextWithEntities result;
|
||||||
result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() });
|
if (mode == ExpandLinksUrlOnly) {
|
||||||
if (mode == ExpandLinksAll) {
|
result.text = url();
|
||||||
result.text = textPart.toString() + qsl(" (") + url() + ')';
|
result.entities.push_back({ EntityInTextUrl, entityOffset, result.text.size() });
|
||||||
|
} else {
|
||||||
|
result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() });
|
||||||
|
if (mode == ExpandLinksAll) {
|
||||||
|
result.text = textPart.toString() + qsl(" (") + url() + ')';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -174,9 +181,19 @@ TextWithEntities HashtagClickHandler::getExpandedLinkTextWithEntities(ExpandLink
|
||||||
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
|
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PeerData *BotCommandClickHandler::_peer = nullptr;
|
||||||
|
UserData *BotCommandClickHandler::_bot = nullptr;
|
||||||
void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
|
void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
|
||||||
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
|
||||||
if (PeerData *peer = Ui::getPeerForMouseAction()) {
|
if (auto peer = peerForCommand()) {
|
||||||
|
if (auto bot = peer->isUser() ? peer->asUser() : botForCommand()) {
|
||||||
|
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
|
||||||
|
App::sendBotCommand(peer, bot, _cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto peer = Ui::getPeerForMouseAction()) { // old way
|
||||||
UserData *bot = peer->isUser() ? peer->asUser() : nullptr;
|
UserData *bot = peer->isUser() ? peer->asUser() : nullptr;
|
||||||
if (auto item = App::hoveredLinkItem()) {
|
if (auto item = App::hoveredLinkItem()) {
|
||||||
if (!bot) {
|
if (!bot) {
|
||||||
|
|
|
@ -193,6 +193,8 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PeerData;
|
||||||
|
class UserData;
|
||||||
class BotCommandClickHandler : public TextClickHandler {
|
class BotCommandClickHandler : public TextClickHandler {
|
||||||
public:
|
public:
|
||||||
BotCommandClickHandler(const QString &cmd) : _cmd(cmd) {
|
BotCommandClickHandler(const QString &cmd) : _cmd(cmd) {
|
||||||
|
@ -204,14 +206,30 @@ public:
|
||||||
return _cmd;
|
return _cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setPeerForCommand(PeerData *peer) {
|
||||||
|
_peer = peer;
|
||||||
|
}
|
||||||
|
static void setBotForCommand(UserData *bot) {
|
||||||
|
_bot = bot;
|
||||||
|
}
|
||||||
|
|
||||||
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
TextWithEntities getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QString url() const override {
|
QString url() const override {
|
||||||
return _cmd;
|
return _cmd;
|
||||||
}
|
}
|
||||||
|
static PeerData *peerForCommand() {
|
||||||
|
return _peer;
|
||||||
|
}
|
||||||
|
static UserData *botForCommand() {
|
||||||
|
return _bot;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString _cmd;
|
QString _cmd;
|
||||||
|
|
||||||
|
static PeerData *_peer;
|
||||||
|
static UserData *_bot;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
115
Telegram/SourceFiles/core/observer.cpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
namespace internal {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct StartCallbackData {
|
||||||
|
void *that;
|
||||||
|
StartCallback call;
|
||||||
|
};
|
||||||
|
struct FinishCallbackData {
|
||||||
|
void *that;
|
||||||
|
FinishCallback call;
|
||||||
|
};
|
||||||
|
struct UnregisterCallbackData {
|
||||||
|
void *that;
|
||||||
|
UnregisterCallback call;
|
||||||
|
};
|
||||||
|
using StartCallbacksList = QVector<StartCallbackData>;
|
||||||
|
using FinishCallbacksList = QVector<FinishCallbackData>;
|
||||||
|
NeverFreedPointer<StartCallbacksList> StartCallbacks;
|
||||||
|
NeverFreedPointer<FinishCallbacksList> FinishCallbacks;
|
||||||
|
UnregisterCallbackData UnregisterCallbacks[256]/* = { nullptr }*/;
|
||||||
|
|
||||||
|
ObservedEvent LastRegisteredEvent/* = 0*/;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
void startObservers() {
|
||||||
|
if (!internal::StartCallbacks) return;
|
||||||
|
|
||||||
|
for (auto &callback : *internal::StartCallbacks) {
|
||||||
|
callback.call(callback.that);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void finishObservers() {
|
||||||
|
if (!internal::FinishCallbacks) return;
|
||||||
|
|
||||||
|
for (auto &callback : *internal::FinishCallbacks) {
|
||||||
|
callback.call(callback.that);
|
||||||
|
}
|
||||||
|
internal::StartCallbacks.clear();
|
||||||
|
internal::FinishCallbacks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
BaseObservedEventRegistrator::BaseObservedEventRegistrator(void *that
|
||||||
|
, StartCallback startCallback
|
||||||
|
, FinishCallback finishCallback
|
||||||
|
, UnregisterCallback unregisterCallback) {
|
||||||
|
_event = LastRegisteredEvent++;
|
||||||
|
|
||||||
|
StartCallbacks.makeIfNull();
|
||||||
|
StartCallbacks->push_back({ that, startCallback });
|
||||||
|
|
||||||
|
FinishCallbacks.makeIfNull();
|
||||||
|
FinishCallbacks->push_back({ that, finishCallback });
|
||||||
|
|
||||||
|
UnregisterCallbacks[_event] = { that, unregisterCallback };
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Observer base interface.
|
||||||
|
Observer::~Observer() {
|
||||||
|
for_const (auto connection, _connections) {
|
||||||
|
unregisterObserver(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Observer::observerRegistered(ConnectionId connection) {
|
||||||
|
_connections.push_back(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterObserver(ConnectionId connection) {
|
||||||
|
auto event = static_cast<internal::ObservedEvent>(connection >> 24);
|
||||||
|
auto connectionIndex = int(connection & 0x00FFFFFFU) - 1;
|
||||||
|
auto &callback = internal::UnregisterCallbacks[event];
|
||||||
|
if (connectionIndex >= 0 && callback.call && callback.that) {
|
||||||
|
callback.call(callback.that, connectionIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
void observerRegisteredDefault(Observer *observer, ConnectionId connection) {
|
||||||
|
observer->observerRegistered(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace Notify
|
242
Telegram/SourceFiles/core/observer.h
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/vector_of_moveable.h"
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
|
||||||
|
using ConnectionId = uint32;
|
||||||
|
|
||||||
|
// startObservers() must be called after main() started (not in a global variable constructor).
|
||||||
|
// finishObservers() must be called before main() finished (not in a global variable destructor).
|
||||||
|
void startObservers();
|
||||||
|
void finishObservers();
|
||||||
|
|
||||||
|
using StartObservedEventCallback = void(*)();
|
||||||
|
using FinishObservedEventCallback = void(*)();
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
using ObservedEvent = uchar;
|
||||||
|
using StartCallback = void(*)(void*);
|
||||||
|
using FinishCallback = void(*)(void*);
|
||||||
|
using UnregisterCallback = void(*)(void*,int connectionIndex);
|
||||||
|
|
||||||
|
class BaseObservedEventRegistrator {
|
||||||
|
public:
|
||||||
|
BaseObservedEventRegistrator(void *that
|
||||||
|
, StartCallback startCallback
|
||||||
|
, FinishCallback finishCallback
|
||||||
|
, UnregisterCallback unregisterCallback);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
inline ObservedEvent event() const {
|
||||||
|
return _event;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ObservedEvent _event;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler is one of Function<> instantiations.
|
||||||
|
template <typename Flags, typename Handler>
|
||||||
|
struct ObserversList {
|
||||||
|
struct Entry {
|
||||||
|
Flags flags;
|
||||||
|
Handler handler;
|
||||||
|
};
|
||||||
|
std_::vector_of_moveable<Entry> entries;
|
||||||
|
QVector<int> freeIndices;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If no filtering by flags is done, you can use Flags=int and this value.
|
||||||
|
constexpr int UniversalFlag = 0x01;
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Objects of this class should be constructed in global scope.
|
||||||
|
// startCallback will be called from Notify::startObservers().
|
||||||
|
// finishCallback will be called from Notify::finishObservers().
|
||||||
|
template <typename Flags, typename Handler>
|
||||||
|
class ObservedEventRegistrator : public internal::BaseObservedEventRegistrator {
|
||||||
|
public:
|
||||||
|
ObservedEventRegistrator(StartObservedEventCallback startCallback,
|
||||||
|
FinishObservedEventCallback finishCallback) : internal::BaseObservedEventRegistrator(reinterpret_cast<void*>(this),
|
||||||
|
ObservedEventRegistrator<Flags, Handler>::start,
|
||||||
|
ObservedEventRegistrator<Flags, Handler>::finish,
|
||||||
|
ObservedEventRegistrator<Flags, Handler>::unregister)
|
||||||
|
, _startCallback(startCallback), _finishCallback(finishCallback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool started() const {
|
||||||
|
return _list != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionId registerObserver(Flags flags, Handler &&handler) {
|
||||||
|
t_assert(started());
|
||||||
|
|
||||||
|
int connectionIndex = doRegisterObserver(flags, std_::forward<Handler>(handler));
|
||||||
|
return (static_cast<uint32>(event()) << 24) | static_cast<uint32>(connectionIndex + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void notify(Flags flags, Args&&... args) {
|
||||||
|
t_assert(started());
|
||||||
|
|
||||||
|
for (auto &entry : _list->entries) {
|
||||||
|
if (!entry.handler.isNull() && (flags & entry.flags)) {
|
||||||
|
entry.handler.call(std_::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Self = ObservedEventRegistrator<Flags, Handler>;
|
||||||
|
static void start(void *vthat) {
|
||||||
|
Self *that = reinterpret_cast<Self*>(vthat);
|
||||||
|
|
||||||
|
t_assert(!that->started());
|
||||||
|
if (that->_startCallback) that->_startCallback();
|
||||||
|
that->_list = new internal::ObserversList<Flags, Handler>();
|
||||||
|
}
|
||||||
|
static void finish(void *vthat) {
|
||||||
|
Self *that = reinterpret_cast<Self*>(vthat);
|
||||||
|
|
||||||
|
if (that->_finishCallback) that->_finishCallback();
|
||||||
|
delete that->_list;
|
||||||
|
that->_list = nullptr;
|
||||||
|
}
|
||||||
|
static void unregister(void *vthat, int connectionIndex) {
|
||||||
|
Self *that = reinterpret_cast<Self*>(vthat);
|
||||||
|
|
||||||
|
t_assert(that->started());
|
||||||
|
|
||||||
|
auto &entries(that->_list->entries);
|
||||||
|
if (entries.size() <= connectionIndex) return;
|
||||||
|
|
||||||
|
if (entries.size() == connectionIndex + 1) {
|
||||||
|
for (entries.pop_back(); !entries.isEmpty() && entries.back().handler.isNull();) {
|
||||||
|
entries.pop_back();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
entries[connectionIndex].handler = Handler();
|
||||||
|
that->_list->freeIndices.push_back(connectionIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int doRegisterObserver(Flags flags, Handler &&handler) {
|
||||||
|
while (!_list->freeIndices.isEmpty()) {
|
||||||
|
auto freeIndex = _list->freeIndices.back();
|
||||||
|
_list->freeIndices.pop_back();
|
||||||
|
|
||||||
|
if (freeIndex < _list->entries.size()) {
|
||||||
|
_list->entries[freeIndex] = { flags, std_::move(handler) };
|
||||||
|
return freeIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_list->entries.push_back({ flags, std_::move(handler) });
|
||||||
|
return _list->entries.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StartObservedEventCallback _startCallback;
|
||||||
|
FinishObservedEventCallback _finishCallback;
|
||||||
|
internal::ObserversList<Flags, Handler> *_list = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// If no filtering of notifications by Flags is intended use this class.
|
||||||
|
template <typename Handler>
|
||||||
|
class SimpleObservedEventRegistrator {
|
||||||
|
public:
|
||||||
|
SimpleObservedEventRegistrator(StartObservedEventCallback startCallback,
|
||||||
|
FinishObservedEventCallback finishCallback) : _implementation(startCallback, finishCallback) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool started() const {
|
||||||
|
return _implementation.started();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionId registerObserver(Handler &&handler) {
|
||||||
|
return _implementation.registerObserver(internal::UniversalFlag, std_::forward<Handler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void notify(Args&&... args) {
|
||||||
|
return _implementation.notify(internal::UniversalFlag, std_::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ObservedEventRegistrator<int, Handler> _implementation;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Each observer type should have observerRegistered(Notify::ConnectionId connection) method.
|
||||||
|
// Usually it is done by deriving the type from the Notify::Observer base class.
|
||||||
|
// In destructor it should call Notify::unregisterObserver(connection) for all the connections.
|
||||||
|
|
||||||
|
class Observer;
|
||||||
|
namespace internal {
|
||||||
|
void observerRegisteredDefault(Observer *observer, ConnectionId connection);
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
void unregisterObserver(ConnectionId connection);
|
||||||
|
|
||||||
|
class Observer {
|
||||||
|
public:
|
||||||
|
virtual ~Observer() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void observerRegistered(ConnectionId connection);
|
||||||
|
friend void internal::observerRegisteredDefault(Observer *observer, ConnectionId connection);
|
||||||
|
|
||||||
|
QVector<ConnectionId> _connections;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename ObserverType, int>
|
||||||
|
struct ObserverRegisteredGeneric {
|
||||||
|
static inline void call(ObserverType *observer, ConnectionId connection) {
|
||||||
|
observer->observerRegistered(connection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
struct ObserverRegisteredGeneric<ObserverType, true> {
|
||||||
|
static inline void call(ObserverType *observer, ConnectionId connection) {
|
||||||
|
observerRegisteredDefault(observer, connection);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
inline void observerRegistered(ObserverType *observer, ConnectionId connection) {
|
||||||
|
// For derivatives of the Observer class we call special friend function observerRegistered().
|
||||||
|
// For all other classes we call just a member function observerRegistered().
|
||||||
|
using ObserverRegistered = internal::ObserverRegisteredGeneric<ObserverType, std_::is_base_of<Observer, ObserverType>::value>;
|
||||||
|
ObserverRegistered::call(observer, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Notify
|
153
Telegram/SourceFiles/core/vector_of_moveable.h
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// some minimal implementation of std::vector() for moveable (but not copiable) types.
|
||||||
|
namespace std_ {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class vector_of_moveable {
|
||||||
|
typedef vector_of_moveable<T> Self;
|
||||||
|
int _size = 0, _capacity = 0;
|
||||||
|
void *_plaindata = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline T *data() {
|
||||||
|
return reinterpret_cast<T*>(_plaindata);
|
||||||
|
}
|
||||||
|
inline const T *data() const {
|
||||||
|
return reinterpret_cast<const T*>(_plaindata);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const Self &other) const {
|
||||||
|
if (this == &other) return true;
|
||||||
|
if (_size != other._size) return false;
|
||||||
|
for (int i = 0; i < _size; ++i) {
|
||||||
|
if (data()[i] != other.data()[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
inline bool operator!=(const Self &other) const { return !(*this == other); }
|
||||||
|
inline int size() const { return _size; }
|
||||||
|
inline bool isEmpty() const { return _size == 0; }
|
||||||
|
inline void clear() {
|
||||||
|
for (int i = 0; i < _size; ++i) {
|
||||||
|
data()[i].~T();
|
||||||
|
}
|
||||||
|
_size = 0;
|
||||||
|
|
||||||
|
operator delete[](_plaindata);
|
||||||
|
_plaindata = nullptr;
|
||||||
|
_capacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef T *iterator;
|
||||||
|
typedef const T *const_iterator;
|
||||||
|
|
||||||
|
// STL style
|
||||||
|
inline iterator begin() { return data(); }
|
||||||
|
inline const_iterator begin() const { return data(); }
|
||||||
|
inline const_iterator cbegin() const { return data(); }
|
||||||
|
inline iterator end() { return data() + _size; }
|
||||||
|
inline const_iterator end() const { return data() + _size; }
|
||||||
|
inline const_iterator cend() const { return data() + _size; }
|
||||||
|
inline iterator erase(iterator it) {
|
||||||
|
T tmp = std_::move(*it);
|
||||||
|
for (auto next = it + 1, e = end(); next != e; ++next) {
|
||||||
|
auto prev = next - 1;
|
||||||
|
*prev = std_::move(*next);
|
||||||
|
}
|
||||||
|
--_size;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline iterator insert(const_iterator pos, T &&value) {
|
||||||
|
int insertAtIndex = pos - begin();
|
||||||
|
if (_size + 1 > _capacity) {
|
||||||
|
reallocate(_capacity + (_capacity > 1 ? _capacity / 2 : 1));
|
||||||
|
}
|
||||||
|
auto insertAt = begin() + insertAtIndex, e = end();
|
||||||
|
if (insertAt == e) {
|
||||||
|
new (&(*insertAt)) T(std_::move(value));
|
||||||
|
} else {
|
||||||
|
auto prev = e - 1;
|
||||||
|
new (&(*e)) T(std_::move(*prev));
|
||||||
|
for (auto it = prev; it != insertAt; --it) {
|
||||||
|
*it = std_::move(*--prev);
|
||||||
|
}
|
||||||
|
*insertAt = std_::move(value);
|
||||||
|
}
|
||||||
|
++_size;
|
||||||
|
return insertAt;
|
||||||
|
}
|
||||||
|
inline void push_back(T &&value) {
|
||||||
|
insert(end(), std_::forward<T>(value));
|
||||||
|
}
|
||||||
|
inline void pop_back() {
|
||||||
|
erase(end() - 1);
|
||||||
|
}
|
||||||
|
inline T &front() {
|
||||||
|
return *begin();
|
||||||
|
}
|
||||||
|
inline const T &front() const {
|
||||||
|
return *begin();
|
||||||
|
}
|
||||||
|
inline T &back() {
|
||||||
|
return *(end() - 1);
|
||||||
|
}
|
||||||
|
inline const T &back() const {
|
||||||
|
return *(end() - 1);
|
||||||
|
}
|
||||||
|
inline bool empty() const { return _size == 0; }
|
||||||
|
|
||||||
|
inline T &operator[](int index) {
|
||||||
|
return data()[index];
|
||||||
|
}
|
||||||
|
inline const T &operator[](int index) const {
|
||||||
|
return data()[index];
|
||||||
|
}
|
||||||
|
inline const T &at(int index) const {
|
||||||
|
if (index < 0 || index >= _size) {
|
||||||
|
throw std::out_of_range("");
|
||||||
|
}
|
||||||
|
return data()[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~vector_of_moveable() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reallocate(int newCapacity) {
|
||||||
|
auto newPlainData = operator new[](newCapacity * sizeof(T));
|
||||||
|
for (int i = 0; i < _size; ++i) {
|
||||||
|
*(reinterpret_cast<T*>(newPlainData) + i) = std_::move(*(data() + i));
|
||||||
|
}
|
||||||
|
std::swap(_plaindata, newPlainData);
|
||||||
|
_capacity = newCapacity;
|
||||||
|
operator delete[](newPlainData);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std_
|
|
@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "core/basic_types.h"
|
#include "core/basic_types.h"
|
||||||
|
|
||||||
#define BETA_VERSION_MACRO (0ULL)
|
#define BETA_VERSION_MACRO (9049001ULL)
|
||||||
|
|
||||||
constexpr int AppVersion = 9049;
|
constexpr int AppVersion = 9049;
|
||||||
constexpr str_const AppVersionStr = "0.9.49";
|
constexpr str_const AppVersionStr = "0.9.49";
|
||||||
|
|
|
@ -641,7 +641,7 @@ void DialogsInner::contextMenuEvent(QContextMenuEvent *e) {
|
||||||
_menu->addAction(lang(lng_profile_clear_history), this, SLOT(onContextClearHistory()))->setEnabled(true);
|
_menu->addAction(lang(lng_profile_clear_history), this, SLOT(onContextClearHistory()))->setEnabled(true);
|
||||||
_menu->addAction(lang(lng_profile_delete_conversation), this, SLOT(onContextDeleteAndLeave()))->setEnabled(true);
|
_menu->addAction(lang(lng_profile_delete_conversation), this, SLOT(onContextDeleteAndLeave()))->setEnabled(true);
|
||||||
if (_menuPeer->asUser()->access != UserNoAccess && _menuPeer != App::self()) {
|
if (_menuPeer->asUser()->access != UserNoAccess && _menuPeer != App::self()) {
|
||||||
_menu->addAction(lang((_menuPeer->asUser()->blocked == UserIsBlocked) ? (_menuPeer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_menuPeer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user)), this, SLOT(onContextToggleBlock()))->setEnabled(true);
|
_menu->addAction(lang(_menuPeer->asUser()->isBlocked() ? (_menuPeer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_menuPeer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user)), this, SLOT(onContextToggleBlock()))->setEnabled(true);
|
||||||
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
|
connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*)));
|
||||||
}
|
}
|
||||||
} else if (_menuPeer->isChat()) {
|
} else if (_menuPeer->isChat()) {
|
||||||
|
@ -662,7 +662,7 @@ bool DialogsInner::menuPeerMuted() {
|
||||||
|
|
||||||
void DialogsInner::onContextProfile() {
|
void DialogsInner::onContextProfile() {
|
||||||
if (!_menuPeer) return;
|
if (!_menuPeer) return;
|
||||||
App::main()->showPeerProfile(_menuPeer);
|
Ui::showPeerProfile(_menuPeer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsInner::onContextToggleNotifications() {
|
void DialogsInner::onContextToggleNotifications() {
|
||||||
|
@ -718,7 +718,7 @@ void DialogsInner::onContextDeleteAndLeaveSure() {
|
||||||
|
|
||||||
void DialogsInner::onContextToggleBlock() {
|
void DialogsInner::onContextToggleBlock() {
|
||||||
if (!_menuPeer || !_menuPeer->isUser()) return;
|
if (!_menuPeer || !_menuPeer->isUser()) return;
|
||||||
if (_menuPeer->asUser()->blocked == UserIsBlocked) {
|
if (_menuPeer->asUser()->isBlocked()) {
|
||||||
MTP::send(MTPcontacts_Unblock(_menuPeer->asUser()->inputUser), rpcDone(&DialogsInner::contextBlockDone, qMakePair(_menuPeer->asUser(), false)));
|
MTP::send(MTPcontacts_Unblock(_menuPeer->asUser()->inputUser), rpcDone(&DialogsInner::contextBlockDone, qMakePair(_menuPeer->asUser(), false)));
|
||||||
} else {
|
} else {
|
||||||
MTP::send(MTPcontacts_Block(_menuPeer->asUser()->inputUser), rpcDone(&DialogsInner::contextBlockDone, qMakePair(_menuPeer->asUser(), true)));
|
MTP::send(MTPcontacts_Block(_menuPeer->asUser()->inputUser), rpcDone(&DialogsInner::contextBlockDone, qMakePair(_menuPeer->asUser(), true)));
|
||||||
|
@ -726,7 +726,7 @@ void DialogsInner::onContextToggleBlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsInner::contextBlockDone(QPair<UserData*, bool> data, const MTPBool &result) {
|
void DialogsInner::contextBlockDone(QPair<UserData*, bool> data, const MTPBool &result) {
|
||||||
data.first->blocked = data.second ? UserIsBlocked : UserIsNotBlocked;
|
data.first->setBlockStatus(data.second ? UserData::BlockStatus::Blocked : UserData::BlockStatus::NotBlocked);
|
||||||
emit App::main()->peerUpdated(data.first);
|
emit App::main()->peerUpdated(data.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -935,7 +935,7 @@ void DialogsInner::updateNotifySettings(PeerData *peer) {
|
||||||
|
|
||||||
void DialogsInner::peerUpdated(PeerData *peer) {
|
void DialogsInner::peerUpdated(PeerData *peer) {
|
||||||
if (_menu && _menuPeer == peer && _menuPeer->isUser() && _menu->actions().size() > 5) {
|
if (_menu && _menuPeer == peer && _menuPeer->isUser() && _menu->actions().size() > 5) {
|
||||||
_menu->actions().at(5)->setText(lang((_menuPeer->asUser()->blocked == UserIsBlocked) ? (_menuPeer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_menuPeer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user)));
|
_menu->actions().at(5)->setText(lang(_menuPeer->asUser()->isBlocked() ? (_menuPeer->asUser()->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_menuPeer->asUser()->botInfo ? lng_profile_block_bot : lng_profile_block_user)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1842,27 +1842,32 @@ void DialogsWidget::dialogsToUp() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsWidget::animShow(const QPixmap &bgAnimCache) {
|
void DialogsWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) {
|
||||||
if (App::app()) App::app()->mtpPause();
|
if (App::app()) App::app()->mtpPause();
|
||||||
|
|
||||||
bool back = true;
|
_cacheUnder = params.oldContentCache;
|
||||||
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
|
show();
|
||||||
|
_cacheOver = App::main()->grabForShowAnimation(params);
|
||||||
|
|
||||||
_a_show.stop();
|
_a_show.stop();
|
||||||
|
|
||||||
(back ? _cacheUnder : _cacheOver) = myGrab(this);
|
|
||||||
|
|
||||||
_scroll.hide();
|
_scroll.hide();
|
||||||
_filter.hide();
|
_filter.hide();
|
||||||
_cancelSearch.hide();
|
_cancelSearch.hide();
|
||||||
_newGroup.hide();
|
_newGroup.hide();
|
||||||
|
|
||||||
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
|
int delta = st::slideShift;
|
||||||
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
if (direction == Window::SlideDirection::FromLeft) {
|
||||||
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
a_progress = anim::fvalue(1, 0);
|
||||||
|
std::swap(_cacheUnder, _cacheOver);
|
||||||
|
a_coordUnder = anim::ivalue(-delta, 0);
|
||||||
|
a_coordOver = anim::ivalue(0, width());
|
||||||
|
} else {
|
||||||
|
a_progress = anim::fvalue(0, 1);
|
||||||
|
a_coordUnder = anim::ivalue(0, -delta);
|
||||||
|
a_coordOver = anim::ivalue(width(), 0);
|
||||||
|
}
|
||||||
_a_show.start();
|
_a_show.start();
|
||||||
|
|
||||||
show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsWidget::step_show(float64 ms, bool timer) {
|
void DialogsWidget::step_show(float64 ms, bool timer) {
|
||||||
|
@ -1872,7 +1877,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) {
|
||||||
|
|
||||||
a_coordUnder.finish();
|
a_coordUnder.finish();
|
||||||
a_coordOver.finish();
|
a_coordOver.finish();
|
||||||
a_shadow.finish();
|
a_progress.finish();
|
||||||
|
|
||||||
_cacheUnder = _cacheOver = QPixmap();
|
_cacheUnder = _cacheOver = QPixmap();
|
||||||
|
|
||||||
|
@ -1887,7 +1892,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) {
|
||||||
} else {
|
} else {
|
||||||
a_coordUnder.update(dt, st::slideFunction);
|
a_coordUnder.update(dt, st::slideFunction);
|
||||||
a_coordOver.update(dt, st::slideFunction);
|
a_coordOver.update(dt, st::slideFunction);
|
||||||
a_shadow.update(dt, st::slideFunction);
|
a_progress.update(dt, st::slideFunction);
|
||||||
}
|
}
|
||||||
if (timer) update();
|
if (timer) update();
|
||||||
}
|
}
|
||||||
|
@ -2474,15 +2479,16 @@ void DialogsWidget::paintEvent(QPaintEvent *e) {
|
||||||
p.setClipRect(r);
|
p.setClipRect(r);
|
||||||
}
|
}
|
||||||
if (_a_show.animating()) {
|
if (_a_show.animating()) {
|
||||||
|
int retina = cIntRetinaFactor();
|
||||||
if (a_coordOver.current() > 0) {
|
if (a_coordOver.current() > 0) {
|
||||||
p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor()));
|
p.drawPixmap(QRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, _cacheUnder.height()));
|
||||||
p.setOpacity(a_shadow.current() * st::slideFadeOut);
|
p.setOpacity(a_progress.current() * st::slideFadeOut);
|
||||||
p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b);
|
p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::black);
|
||||||
p.setOpacity(1);
|
p.setOpacity(1);
|
||||||
}
|
}
|
||||||
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
|
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height()));
|
||||||
p.setOpacity(a_shadow.current());
|
p.setOpacity(a_progress.current());
|
||||||
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
|
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), _cacheOver.height() / retina), App::sprite(), st::slideShadow.rect());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QRect above(0, 0, width(), _scroll.y());
|
QRect above(0, 0, width(), _scroll.y());
|
||||||
|
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "window/section_widget.h"
|
||||||
|
|
||||||
class MainWidget;
|
class MainWidget;
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
class Row;
|
class Row;
|
||||||
|
@ -260,7 +262,10 @@ public:
|
||||||
|
|
||||||
void dialogsToUp();
|
void dialogsToUp();
|
||||||
|
|
||||||
void animShow(const QPixmap &bgAnimCache);
|
bool hasTopBarShadow() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms);
|
||||||
void step_show(float64 ms, bool timer);
|
void step_show(float64 ms, bool timer);
|
||||||
|
|
||||||
void destroyData();
|
void destroyData();
|
||||||
|
@ -338,7 +343,7 @@ private:
|
||||||
Animation _a_show;
|
Animation _a_show;
|
||||||
QPixmap _cacheUnder, _cacheOver;
|
QPixmap _cacheUnder, _cacheOver;
|
||||||
anim::ivalue a_coordUnder, a_coordOver;
|
anim::ivalue a_coordUnder, a_coordOver;
|
||||||
anim::fvalue a_shadow;
|
anim::fvalue a_progress;
|
||||||
|
|
||||||
PeerData *_searchInPeer, *_searchInMigrated;
|
PeerData *_searchInPeer, *_searchInMigrated;
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "profile/profile_section_memento.h"
|
||||||
|
#include "core/vector_of_moveable.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "core/click_handler_types.h"
|
|
||||||
#include "boxes/confirmbox.h"
|
#include "boxes/confirmbox.h"
|
||||||
#include "layerwidget.h"
|
#include "layerwidget.h"
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
|
@ -237,7 +240,15 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void showPeerProfile(const PeerId &peer) {
|
void showPeerProfile(const PeerId &peer) {
|
||||||
if (MainWidget *m = App::main()) m->showPeerProfile(App::peer(peer));
|
if (auto m = App::main()) {
|
||||||
|
m->showWideSection(Profile::SectionMemento(App::peer(peer)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showPeerOverview(const PeerId &peer, MediaOverviewType type) {
|
||||||
|
if (auto m = App::main()) {
|
||||||
|
m->showMediaOverview(App::peer(peer), type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) {
|
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back) {
|
||||||
|
@ -273,6 +284,15 @@ bool hideWindowNoQuit() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool skipPaintEvent(QWidget *widget, QPaintEvent *event) {
|
||||||
|
if (auto w = App::wnd()) {
|
||||||
|
if (w->contentOverlapped(widget, event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Notify {
|
namespace Notify {
|
||||||
|
@ -286,7 +306,10 @@ void userIsContactChanged(UserData *user, bool fromThisApp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void botCommandsChanged(UserData *user) {
|
void botCommandsChanged(UserData *user) {
|
||||||
if (MainWidget *m = App::main()) m->notify_botCommandsChanged(user);
|
if (MainWidget *m = App::main()) {
|
||||||
|
m->notify_botCommandsChanged(user);
|
||||||
|
}
|
||||||
|
peerUpdatedDelayed(user, PeerUpdate::Flag::BotCommandsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inlineBotRequesting(bool requesting) {
|
void inlineBotRequesting(bool requesting) {
|
||||||
|
@ -500,6 +523,8 @@ struct Data {
|
||||||
uint64 LaunchId = 0;
|
uint64 LaunchId = 0;
|
||||||
SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" };
|
SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" };
|
||||||
SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" };
|
SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" };
|
||||||
|
SingleDelayedCall HandleFileDialogQueue = { App::app(), "call_handleFileDialogQueue" };
|
||||||
|
SingleDelayedCall HandleDelayedPeerUpdates = { App::app(), "call_handleDelayedPeerUpdates" };
|
||||||
|
|
||||||
Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout;
|
Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout;
|
||||||
bool AdaptiveForWide = true;
|
bool AdaptiveForWide = true;
|
||||||
|
@ -563,6 +588,8 @@ void finish() {
|
||||||
DefineReadOnlyVar(Global, uint64, LaunchId);
|
DefineReadOnlyVar(Global, uint64, LaunchId);
|
||||||
DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate);
|
DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate);
|
||||||
DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate);
|
DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate);
|
||||||
|
DefineRefVar(Global, SingleDelayedCall, HandleFileDialogQueue);
|
||||||
|
DefineRefVar(Global, SingleDelayedCall, HandleDelayedPeerUpdates);
|
||||||
|
|
||||||
DefineVar(Global, Adaptive::Layout, AdaptiveLayout);
|
DefineVar(Global, Adaptive::Layout, AdaptiveLayout);
|
||||||
DefineVar(Global, bool, AdaptiveForWide);
|
DefineVar(Global, bool, AdaptiveForWide);
|
||||||
|
|
|
@ -75,6 +75,14 @@ inline void showPeerProfile(const History *history) {
|
||||||
showPeerProfile(history->peer->id);
|
showPeerProfile(history->peer->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showPeerOverview(const PeerId &peer, MediaOverviewType type);
|
||||||
|
inline void showPeerOverview(const PeerData *peer, MediaOverviewType type) {
|
||||||
|
showPeerOverview(peer->id, type);
|
||||||
|
}
|
||||||
|
inline void showPeerOverview(const History *history, MediaOverviewType type) {
|
||||||
|
showPeerOverview(history->peer->id, type);
|
||||||
|
}
|
||||||
|
|
||||||
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false);
|
void showPeerHistory(const PeerId &peer, MsgId msgId, bool back = false);
|
||||||
inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) {
|
inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) {
|
||||||
showPeerHistory(peer->id, msgId, back);
|
showPeerHistory(peer->id, msgId, back);
|
||||||
|
@ -96,6 +104,8 @@ PeerData *getPeerForMouseAction();
|
||||||
|
|
||||||
bool hideWindowNoQuit();
|
bool hideWindowNoQuit();
|
||||||
|
|
||||||
|
bool skipPaintEvent(QWidget *widget, QPaintEvent *event);
|
||||||
|
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
enum ClipStopperType {
|
enum ClipStopperType {
|
||||||
|
@ -194,6 +204,8 @@ void finish();
|
||||||
DeclareReadOnlyVar(uint64, LaunchId);
|
DeclareReadOnlyVar(uint64, LaunchId);
|
||||||
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
|
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
|
||||||
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
|
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
|
||||||
|
DeclareRefVar(SingleDelayedCall, HandleFileDialogQueue);
|
||||||
|
DeclareRefVar(SingleDelayedCall, HandleDelayedPeerUpdates);
|
||||||
|
|
||||||
DeclareVar(Adaptive::Layout, AdaptiveLayout);
|
DeclareVar(Adaptive::Layout, AdaptiveLayout);
|
||||||
DeclareVar(bool, AdaptiveForWide);
|
DeclareVar(bool, AdaptiveForWide);
|
||||||
|
|
|
@ -36,6 +36,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "window/top_bar_widget.h"
|
#include "window/top_bar_widget.h"
|
||||||
#include "playerwidget.h"
|
#include "playerwidget.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -751,6 +752,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
if (peer->asChannel()->mgInfo->lastParticipants.indexOf(user) < 0) {
|
if (peer->asChannel()->mgInfo->lastParticipants.indexOf(user) < 0) {
|
||||||
peer->asChannel()->mgInfo->lastParticipants.push_front(user);
|
peer->asChannel()->mgInfo->lastParticipants.push_front(user);
|
||||||
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated;
|
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated;
|
||||||
|
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
}
|
}
|
||||||
if (user->botInfo) {
|
if (user->botInfo) {
|
||||||
peer->asChannel()->mgInfo->bots.insert(user);
|
peer->asChannel()->mgInfo->bots.insert(user);
|
||||||
|
@ -769,6 +771,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
if (result->from()->isUser()) {
|
if (result->from()->isUser()) {
|
||||||
if (peer->asChannel()->mgInfo->lastParticipants.indexOf(result->from()->asUser()) < 0) {
|
if (peer->asChannel()->mgInfo->lastParticipants.indexOf(result->from()->asUser()) < 0) {
|
||||||
peer->asChannel()->mgInfo->lastParticipants.push_front(result->from()->asUser());
|
peer->asChannel()->mgInfo->lastParticipants.push_front(result->from()->asUser());
|
||||||
|
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
}
|
}
|
||||||
if (result->from()->asUser()->botInfo) {
|
if (result->from()->asUser()->botInfo) {
|
||||||
peer->asChannel()->mgInfo->bots.insert(result->from()->asUser());
|
peer->asChannel()->mgInfo->bots.insert(result->from()->asUser());
|
||||||
|
@ -793,26 +796,31 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
if (App::main()) App::main()->updateBotKeyboard(this);
|
if (App::main()) App::main()->updateBotKeyboard(this);
|
||||||
}
|
}
|
||||||
if (peer->isMegagroup()) {
|
if (peer->isMegagroup()) {
|
||||||
if (UserData *user = App::userLoaded(uid)) {
|
if (auto user = App::userLoaded(uid)) {
|
||||||
int32 index = peer->asChannel()->mgInfo->lastParticipants.indexOf(user);
|
auto channel = peer->asChannel();
|
||||||
|
auto megagroupInfo = channel->mgInfo;
|
||||||
|
|
||||||
|
int32 index = megagroupInfo->lastParticipants.indexOf(user);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
peer->asChannel()->mgInfo->lastParticipants.removeAt(index);
|
megagroupInfo->lastParticipants.removeAt(index);
|
||||||
|
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
}
|
}
|
||||||
if (peer->asChannel()->count > 1) {
|
if (peer->asChannel()->membersCount() > 1) {
|
||||||
--peer->asChannel()->count;
|
peer->asChannel()->setMembersCount(channel->membersCount() - 1);
|
||||||
} else {
|
} else {
|
||||||
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
|
megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
|
||||||
peer->asChannel()->mgInfo->lastParticipantsCount = 0;
|
megagroupInfo->lastParticipantsCount = 0;
|
||||||
}
|
}
|
||||||
if (peer->asChannel()->mgInfo->lastAdmins.contains(user)) {
|
if (megagroupInfo->lastAdmins.contains(user)) {
|
||||||
peer->asChannel()->mgInfo->lastAdmins.remove(user);
|
megagroupInfo->lastAdmins.remove(user);
|
||||||
if (peer->asChannel()->adminsCount > 1) {
|
if (channel->adminsCount() > 1) {
|
||||||
--peer->asChannel()->adminsCount;
|
channel->setAdminsCount(channel->adminsCount() - 1);
|
||||||
}
|
}
|
||||||
|
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||||
}
|
}
|
||||||
peer->asChannel()->mgInfo->bots.remove(user);
|
megagroupInfo->bots.remove(user);
|
||||||
if (peer->asChannel()->mgInfo->bots.isEmpty() && peer->asChannel()->mgInfo->botStatus > 0) {
|
if (megagroupInfo->bots.isEmpty() && megagroupInfo->botStatus > 0) {
|
||||||
peer->asChannel()->mgInfo->botStatus = -1;
|
megagroupInfo->botStatus = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -848,9 +856,10 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case mtpc_messageActionChatEditTitle: {
|
case mtpc_messageActionChatEditTitle: {
|
||||||
const auto &d(action.c_messageActionChatEditTitle());
|
auto &d(action.c_messageActionChatEditTitle());
|
||||||
ChatData *chat = peer->asChat();
|
if (auto chat = peer->asChat()) {
|
||||||
if (chat) chat->updateName(qs(d.vtitle), QString(), QString());
|
chat->setName(qs(d.vtitle));
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case mtpc_messageActionChatMigrateTo: {
|
case mtpc_messageActionChatMigrateTo: {
|
||||||
|
@ -1019,6 +1028,9 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) {
|
||||||
if (prev) {
|
if (prev) {
|
||||||
lastAuthors->push_front(adding->from()->asUser());
|
lastAuthors->push_front(adding->from()->asUser());
|
||||||
}
|
}
|
||||||
|
if (peer->isMegagroup()) {
|
||||||
|
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (adding->definesReplyKeyboard()) {
|
if (adding->definesReplyKeyboard()) {
|
||||||
|
@ -1194,6 +1206,7 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
|
||||||
lastAuthors->push_back(item->from()->asUser());
|
lastAuthors->push_back(item->from()->asUser());
|
||||||
if (peer->isMegagroup()) {
|
if (peer->isMegagroup()) {
|
||||||
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated;
|
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated;
|
||||||
|
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1704,6 +1717,7 @@ void History::getReadyFor(MsgId msgId) {
|
||||||
}
|
}
|
||||||
if (!isReadyFor(msgId)) {
|
if (!isReadyFor(msgId)) {
|
||||||
clear(true);
|
clear(true);
|
||||||
|
|
||||||
if (msgId == ShowAtTheEndMsgId) {
|
if (msgId == ShowAtTheEndMsgId) {
|
||||||
newLoaded = true;
|
newLoaded = true;
|
||||||
}
|
}
|
||||||
|
@ -2847,14 +2861,14 @@ HistoryItem::~HistoryItem() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RadialAnimation::RadialAnimation(AnimationCreator creator)
|
RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
|
||||||
: _firstStart(0)
|
: _firstStart(0)
|
||||||
, _lastStart(0)
|
, _lastStart(0)
|
||||||
, _lastTime(0)
|
, _lastTime(0)
|
||||||
, _opacity(0)
|
, _opacity(0)
|
||||||
, a_arcEnd(0, 0)
|
, a_arcEnd(0, 0)
|
||||||
, a_arcStart(0, FullArcLength)
|
, a_arcStart(0, FullArcLength)
|
||||||
, _animation(creator) {
|
, _animation(std_::move(callbacks)) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1590,7 +1590,7 @@ protected:
|
||||||
class RadialAnimation {
|
class RadialAnimation {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RadialAnimation(AnimationCreator creator);
|
RadialAnimation(AnimationCallbacks &&callbacks);
|
||||||
|
|
||||||
float64 opacity() const {
|
float64 opacity() const {
|
||||||
return _opacity;
|
return _opacity;
|
||||||
|
@ -1811,9 +1811,9 @@ protected:
|
||||||
virtual bool dataLoaded() const = 0;
|
virtual bool dataLoaded() const = 0;
|
||||||
|
|
||||||
struct AnimationData {
|
struct AnimationData {
|
||||||
AnimationData(AnimationCreator thumbOverCallbacks, AnimationCreator radialCallbacks) : a_thumbOver(0, 0)
|
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
|
||||||
, _a_thumbOver(thumbOverCallbacks)
|
, _a_thumbOver(std_::move(thumbOverCallbacks))
|
||||||
, radial(radialCallbacks) {
|
, radial(std_::move(radialCallbacks)) {
|
||||||
}
|
}
|
||||||
anim::fvalue a_thumbOver;
|
anim::fvalue a_thumbOver;
|
||||||
Animation _a_thumbOver;
|
Animation _a_thumbOver;
|
||||||
|
|
|
@ -39,6 +39,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "localstorage.h"
|
#include "localstorage.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "window/top_bar_widget.h"
|
#include "window/top_bar_widget.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
#include "playerwidget.h"
|
#include "playerwidget.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -658,7 +659,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
|
||||||
_selected.insert(_dragItem, selStatus);
|
_selected.insert(_dragItem, selStatus);
|
||||||
_dragSymbol = dragState.symbol;
|
_dragSymbol = dragState.symbol;
|
||||||
_dragAction = Selecting;
|
_dragAction = Selecting;
|
||||||
_dragSelType = TextSelectParagraphs;
|
_dragSelType = TextSelectType::Paragraphs;
|
||||||
dragActionUpdate(_dragPos);
|
dragActionUpdate(_dragPos);
|
||||||
_trippleClickTimer.start(QApplication::doubleClickInterval());
|
_trippleClickTimer.start(QApplication::doubleClickInterval());
|
||||||
}
|
}
|
||||||
|
@ -668,7 +669,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
|
||||||
request.flags = Text::StateRequest::Flag::LookupSymbol;
|
request.flags = Text::StateRequest::Flag::LookupSymbol;
|
||||||
dragState = _dragItem->getState(_dragStartPos.x(), _dragStartPos.y(), request);
|
dragState = _dragItem->getState(_dragStartPos.x(), _dragStartPos.y(), request);
|
||||||
}
|
}
|
||||||
if (_dragSelType != TextSelectParagraphs) {
|
if (_dragSelType != TextSelectType::Paragraphs) {
|
||||||
if (App::pressedItem()) {
|
if (App::pressedItem()) {
|
||||||
_dragSymbol = dragState.symbol;
|
_dragSymbol = dragState.symbol;
|
||||||
bool uponSelected = (dragState.cursor == HistoryInTextCursorState);
|
bool uponSelected = (dragState.cursor == HistoryInTextCursorState);
|
||||||
|
@ -715,7 +716,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
|
||||||
if (!_dragItem) {
|
if (!_dragItem) {
|
||||||
_dragAction = NoDrag;
|
_dragAction = NoDrag;
|
||||||
} else if (_dragAction == NoDrag) {
|
} else if (_dragAction == NoDrag) {
|
||||||
_dragItem = 0;
|
_dragItem = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,6 +777,9 @@ void HistoryInner::onDragExec() {
|
||||||
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
|
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
|
||||||
}
|
}
|
||||||
drag->setMimeData(mimeData);
|
drag->setMimeData(mimeData);
|
||||||
|
|
||||||
|
// We don't receive mouseReleaseEvent when drag is finished.
|
||||||
|
ClickHandler::unpressed();
|
||||||
drag->exec(Qt::CopyAction);
|
drag->exec(Qt::CopyAction);
|
||||||
if (App::main()) App::main()->updateAfterDrag();
|
if (App::main()) App::main()->updateAfterDrag();
|
||||||
return;
|
return;
|
||||||
|
@ -810,6 +814,9 @@ void HistoryInner::onDragExec() {
|
||||||
}
|
}
|
||||||
|
|
||||||
drag->setMimeData(mimeData);
|
drag->setMimeData(mimeData);
|
||||||
|
|
||||||
|
// We don't receive mouseReleaseEvent when drag is finished.
|
||||||
|
ClickHandler::unpressed();
|
||||||
drag->exec(Qt::CopyAction);
|
drag->exec(Qt::CopyAction);
|
||||||
if (App::main()) App::main()->updateAfterDrag();
|
if (App::main()) App::main()->updateAfterDrag();
|
||||||
return;
|
return;
|
||||||
|
@ -906,7 +913,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
|
||||||
}
|
}
|
||||||
_dragAction = NoDrag;
|
_dragAction = NoDrag;
|
||||||
_dragItem = 0;
|
_dragItem = 0;
|
||||||
_dragSelType = TextSelectLetters;
|
_dragSelType = TextSelectType::Letters;
|
||||||
_widget->noSelectingScroll();
|
_widget->noSelectingScroll();
|
||||||
_widget->updateTopBarSelection();
|
_widget->updateTopBarSelection();
|
||||||
}
|
}
|
||||||
|
@ -922,13 +929,13 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||||
if (!_history) return;
|
if (!_history) return;
|
||||||
|
|
||||||
dragActionStart(e->globalPos(), e->button());
|
dragActionStart(e->globalPos(), e->button());
|
||||||
if (((_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) || (_dragAction == NoDrag && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection))) && _dragSelType == TextSelectLetters && _dragItem) {
|
if (((_dragAction == Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) || (_dragAction == NoDrag && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection))) && _dragSelType == TextSelectType::Letters && _dragItem) {
|
||||||
HistoryStateRequest request;
|
HistoryStateRequest request;
|
||||||
request.flags |= Text::StateRequest::Flag::LookupSymbol;
|
request.flags |= Text::StateRequest::Flag::LookupSymbol;
|
||||||
auto dragState = _dragItem->getState(_dragStartPos.x(), _dragStartPos.y(), request);
|
auto dragState = _dragItem->getState(_dragStartPos.x(), _dragStartPos.y(), request);
|
||||||
if (dragState.cursor == HistoryInTextCursorState) {
|
if (dragState.cursor == HistoryInTextCursorState) {
|
||||||
_dragSymbol = dragState.symbol;
|
_dragSymbol = dragState.symbol;
|
||||||
_dragSelType = TextSelectWords;
|
_dragSelType = TextSelectType::Words;
|
||||||
if (_dragAction == NoDrag) {
|
if (_dragAction == NoDrag) {
|
||||||
_dragAction = Selecting;
|
_dragAction = Selecting;
|
||||||
TextSelection selStatus = { dragState.symbol, dragState.symbol };
|
TextSelection selStatus = { dragState.symbol, dragState.symbol };
|
||||||
|
@ -1787,7 +1794,7 @@ void HistoryInner::onUpdateSelected() {
|
||||||
bool canSelectMany = (_history != nullptr);
|
bool canSelectMany = (_history != nullptr);
|
||||||
if (selectingText) {
|
if (selectingText) {
|
||||||
uint16 second = dragState.symbol;
|
uint16 second = dragState.symbol;
|
||||||
if (dragState.afterSymbol && _dragSelType == TextSelectLetters) {
|
if (dragState.afterSymbol && _dragSelType == TextSelectType::Letters) {
|
||||||
++second;
|
++second;
|
||||||
}
|
}
|
||||||
auto selState = _dragItem->adjustSelection({ qMin(second, _dragSymbol), qMax(second, _dragSymbol) }, _dragSelType);
|
auto selState = _dragItem->adjustSelection({ qMin(second, _dragSymbol), qMax(second, _dragSymbol) }, _dragSelType);
|
||||||
|
@ -2816,7 +2823,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||||
, _attachDragPhoto(this)
|
, _attachDragPhoto(this)
|
||||||
, _fileLoader(this, FileLoaderQueueStopTimeout)
|
, _fileLoader(this, FileLoaderQueueStopTimeout)
|
||||||
, _a_show(animation(this, &HistoryWidget::step_show))
|
, _a_show(animation(this, &HistoryWidget::step_show))
|
||||||
, _sideShadow(this, st::shadowColor)
|
|
||||||
, _topShadow(this, st::shadowColor) {
|
, _topShadow(this, st::shadowColor) {
|
||||||
_scroll.setFocusPolicy(Qt::NoFocus);
|
_scroll.setFocusPolicy(Qt::NoFocus);
|
||||||
|
|
||||||
|
@ -2937,7 +2943,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
|
||||||
_attachDragPhoto.hide();
|
_attachDragPhoto.hide();
|
||||||
|
|
||||||
_topShadow.hide();
|
_topShadow.hide();
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
|
|
||||||
connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*)));
|
connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(const QMimeData*)));
|
||||||
connect(&_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*)));
|
connect(&_attachDragPhoto, SIGNAL(dropped(const QMimeData*)), this, SLOT(onPhotoDrop(const QMimeData*)));
|
||||||
|
@ -3837,7 +3842,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
|
||||||
if (_channel) {
|
if (_channel) {
|
||||||
updateNotifySettings();
|
updateNotifySettings();
|
||||||
if (_peer->notify == UnknownNotifySettings) {
|
if (_peer->notify == UnknownNotifySettings) {
|
||||||
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(_peer->input));
|
App::api()->requestNotifySetting(_peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4059,7 +4064,9 @@ bool HistoryWidget::reportSpamSettingFail(const RPCError &error, mtpRequestId re
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::updateControlsVisibility() {
|
void HistoryWidget::updateControlsVisibility() {
|
||||||
_topShadow.setVisible(_peer ? true : false);
|
if (!_a_show.animating()) {
|
||||||
|
_topShadow.setVisible(_peer ? true : false);
|
||||||
|
}
|
||||||
if (!_history || _a_show.animating()) {
|
if (!_history || _a_show.animating()) {
|
||||||
_reportSpamPanel.hide();
|
_reportSpamPanel.hide();
|
||||||
_scroll.hide();
|
_scroll.hide();
|
||||||
|
@ -4455,6 +4462,7 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages
|
||||||
} else if (_migrated) {
|
} else if (_migrated) {
|
||||||
_migrated->clear(true);
|
_migrated->clear(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_delayedShowAtRequest = 0;
|
_delayedShowAtRequest = 0;
|
||||||
_history->getReadyFor(_delayedShowAtMsgId);
|
_history->getReadyFor(_delayedShowAtMsgId);
|
||||||
if (_history->isEmpty()) {
|
if (_history->isEmpty()) {
|
||||||
|
@ -4822,7 +4830,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
|
||||||
|
|
||||||
void HistoryWidget::onUnblock() {
|
void HistoryWidget::onUnblock() {
|
||||||
if (_unblockRequest) return;
|
if (_unblockRequest) return;
|
||||||
if (!_peer || !_peer->isUser() || _peer->asUser()->blocked != UserIsBlocked) {
|
if (!_peer || !_peer->isUser() || !_peer->asUser()->isBlocked()) {
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -4833,7 +4841,7 @@ void HistoryWidget::onUnblock() {
|
||||||
void HistoryWidget::unblockDone(PeerData *peer, const MTPBool &result, mtpRequestId req) {
|
void HistoryWidget::unblockDone(PeerData *peer, const MTPBool &result, mtpRequestId req) {
|
||||||
if (!peer->isUser()) return;
|
if (!peer->isUser()) return;
|
||||||
if (_unblockRequest == req) _unblockRequest = 0;
|
if (_unblockRequest == req) _unblockRequest = 0;
|
||||||
peer->asUser()->blocked = UserIsNotBlocked;
|
peer->asUser()->setBlockStatus(UserData::BlockStatus::NotBlocked);
|
||||||
emit App::main()->peerUpdated(peer);
|
emit App::main()->peerUpdated(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4847,7 +4855,7 @@ bool HistoryWidget::unblockFail(const RPCError &error, mtpRequestId req) {
|
||||||
void HistoryWidget::blockDone(PeerData *peer, const MTPBool &result) {
|
void HistoryWidget::blockDone(PeerData *peer, const MTPBool &result) {
|
||||||
if (!peer->isUser()) return;
|
if (!peer->isUser()) return;
|
||||||
|
|
||||||
peer->asUser()->blocked = UserIsBlocked;
|
peer->asUser()->setBlockStatus(UserData::BlockStatus::Blocked);
|
||||||
emit App::main()->peerUpdated(peer);
|
emit App::main()->peerUpdated(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4911,12 +4919,14 @@ void HistoryWidget::onBroadcastSilentChange() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) {
|
||||||
if (!contact || contact->phone.isEmpty()) return;
|
auto phone = contact->phone();
|
||||||
|
if (phone.isEmpty()) phone = App::phoneFromSharedContact(peerToUser(contact->id));
|
||||||
|
if (!contact || phone.isEmpty()) return;
|
||||||
|
|
||||||
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
|
Ui::showPeerHistory(peer, ShowAtTheEndMsgId);
|
||||||
if (!_history) return;
|
if (!_history) return;
|
||||||
|
|
||||||
shareContact(peer, contact->phone, contact->firstName, contact->lastName, replyToId(), peerToUser(contact->id));
|
shareContact(peer, phone, contact->firstName, contact->lastName, replyToId(), peerToUser(contact->id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId) {
|
void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const QString &fname, const QString &lname, MsgId replyTo, int32 userId) {
|
||||||
|
@ -4993,15 +5003,15 @@ MsgId HistoryWidget::msgId() const {
|
||||||
return _showAtMsgId;
|
return _showAtMsgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back) {
|
void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) {
|
||||||
if (App::app()) App::app()->mtpPause();
|
if (App::app()) App::app()->mtpPause();
|
||||||
|
|
||||||
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
|
_cacheUnder = params.oldContentCache;
|
||||||
(back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache;
|
show();
|
||||||
(back ? _cacheUnder : _cacheOver) = myGrab(this);
|
_topShadow.setVisible(params.withTopBarShadow ? false : true);
|
||||||
App::main()->topBar()->stopAnim();
|
_cacheOver = App::main()->grabForShowAnimation(params);
|
||||||
(back ? _cacheTopBarUnder : _cacheTopBarOver) = myGrab(App::main()->topBar());
|
|
||||||
App::main()->topBar()->startAnim();
|
App::main()->topBar()->startAnim();
|
||||||
|
_topShadow.setVisible(params.withTopBarShadow ? true : false);
|
||||||
|
|
||||||
_scroll.hide();
|
_scroll.hide();
|
||||||
_kbScroll.hide();
|
_kbScroll.hide();
|
||||||
|
@ -5023,18 +5033,26 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
|
||||||
_botStart.hide();
|
_botStart.hide();
|
||||||
_joinChannel.hide();
|
_joinChannel.hide();
|
||||||
_muteUnmute.hide();
|
_muteUnmute.hide();
|
||||||
_topShadow.hide();
|
|
||||||
if (_pinnedBar) {
|
if (_pinnedBar) {
|
||||||
_pinnedBar->shadow.hide();
|
_pinnedBar->shadow.hide();
|
||||||
_pinnedBar->cancel.hide();
|
_pinnedBar->cancel.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
|
int delta = st::slideShift;
|
||||||
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
if (direction == Window::SlideDirection::FromLeft) {
|
||||||
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
a_progress = anim::fvalue(1, 0);
|
||||||
|
std::swap(_cacheUnder, _cacheOver);
|
||||||
|
a_coordUnder = anim::ivalue(-delta, 0);
|
||||||
|
a_coordOver = anim::ivalue(0, width());
|
||||||
|
} else {
|
||||||
|
a_progress = anim::fvalue(0, 1);
|
||||||
|
a_coordUnder = anim::ivalue(0, -delta);
|
||||||
|
a_coordOver = anim::ivalue(width(), 0);
|
||||||
|
}
|
||||||
_a_show.start();
|
_a_show.start();
|
||||||
|
|
||||||
App::main()->topBar()->update();
|
App::main()->topBar()->update();
|
||||||
|
|
||||||
activate();
|
activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5042,13 +5060,12 @@ void HistoryWidget::step_show(float64 ms, bool timer) {
|
||||||
float64 dt = ms / st::slideDuration;
|
float64 dt = ms / st::slideDuration;
|
||||||
if (dt >= 1) {
|
if (dt >= 1) {
|
||||||
_a_show.stop();
|
_a_show.stop();
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
_topShadow.setVisible(_peer ? true : false);
|
_topShadow.setVisible(_peer ? true : false);
|
||||||
|
|
||||||
a_coordUnder.finish();
|
a_coordUnder.finish();
|
||||||
a_coordOver.finish();
|
a_coordOver.finish();
|
||||||
a_shadow.finish();
|
a_progress.finish();
|
||||||
_cacheUnder = _cacheOver = _cacheTopBarUnder = _cacheTopBarOver = QPixmap();
|
_cacheUnder = _cacheOver = QPixmap();
|
||||||
App::main()->topBar()->stopAnim();
|
App::main()->topBar()->stopAnim();
|
||||||
doneShow();
|
doneShow();
|
||||||
|
|
||||||
|
@ -5056,7 +5073,7 @@ void HistoryWidget::step_show(float64 ms, bool timer) {
|
||||||
} else {
|
} else {
|
||||||
a_coordUnder.update(dt, st::slideFunction);
|
a_coordUnder.update(dt, st::slideFunction);
|
||||||
a_coordOver.update(dt, st::slideFunction);
|
a_coordOver.update(dt, st::slideFunction);
|
||||||
a_shadow.update(dt, st::slideFunction);
|
a_progress.update(dt, st::slideFunction);
|
||||||
}
|
}
|
||||||
if (timer) {
|
if (timer) {
|
||||||
update();
|
update();
|
||||||
|
@ -5081,14 +5098,12 @@ void HistoryWidget::doneShow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::updateAdaptiveLayout() {
|
void HistoryWidget::updateAdaptiveLayout() {
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::animStop() {
|
void HistoryWidget::animStop() {
|
||||||
if (!_a_show.animating()) return;
|
if (!_a_show.animating()) return;
|
||||||
_a_show.stop();
|
_a_show.stop();
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
_topShadow.setVisible(_peer ? true : false);
|
_topShadow.setVisible(_peer ? true : false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5450,8 +5465,7 @@ DragState HistoryWidget::getDragState(const QMimeData *d) {
|
||||||
for (QList<QUrl>::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) {
|
for (QList<QUrl>::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) {
|
||||||
if (!i->isLocalFile()) return DragStateNone;
|
if (!i->isLocalFile()) return DragStateNone;
|
||||||
|
|
||||||
QString file(i->toLocalFile());
|
auto file = psConvertFileUrl(*i);
|
||||||
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
|
|
||||||
|
|
||||||
QFileInfo info(file);
|
QFileInfo info(file);
|
||||||
if (info.isDir()) return DragStateNone;
|
if (info.isDir()) return DragStateNone;
|
||||||
|
@ -5569,7 +5583,7 @@ bool HistoryWidget::isBotStart() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryWidget::isBlocked() const {
|
bool HistoryWidget::isBlocked() const {
|
||||||
return _peer && _peer->isUser() && _peer->asUser()->blocked == UserIsBlocked;
|
return _peer && _peer->isUser() && _peer->asUser()->isBlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryWidget::isJoinChannel() const {
|
bool HistoryWidget::isJoinChannel() const {
|
||||||
|
@ -5769,9 +5783,15 @@ void HistoryWidget::onForwardHere() {
|
||||||
|
|
||||||
void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) {
|
void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) {
|
||||||
if (_a_show.animating()) {
|
if (_a_show.animating()) {
|
||||||
p.drawPixmap(a_coordUnder.current(), 0, _cacheTopBarUnder);
|
int retina = cIntRetinaFactor();
|
||||||
p.drawPixmap(a_coordOver.current(), 0, _cacheTopBarOver);
|
if (a_coordOver.current() > 0) {
|
||||||
p.setOpacity(a_shadow.current());
|
p.drawPixmap(QRect(0, 0, a_coordOver.current(), st::topBarHeight), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, st::topBarHeight * retina));
|
||||||
|
p.setOpacity(a_progress.current() * st::slideFadeOut);
|
||||||
|
p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::black);
|
||||||
|
p.setOpacity(1);
|
||||||
|
}
|
||||||
|
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina));
|
||||||
|
p.setOpacity(a_progress.current());
|
||||||
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), st::topBarHeight), App::sprite(), st::slideShadow.rect());
|
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), st::topBarHeight), App::sprite(), st::slideShadow.rect());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5806,7 +5826,7 @@ void HistoryWidget::topBarClick() {
|
||||||
if (Adaptive::OneColumn()) {
|
if (Adaptive::OneColumn()) {
|
||||||
Ui::showChatsList();
|
Ui::showChatsList();
|
||||||
} else {
|
} else {
|
||||||
if (_history) App::main()->showPeerProfile(_peer);
|
if (_history) Ui::showPeerProfile(_peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5839,8 +5859,8 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (_peer->isChannel()) {
|
} else if (_peer->isChannel()) {
|
||||||
if (_peer->isMegagroup() && _peer->asChannel()->count > 0 && _peer->asChannel()->count <= Global::ChatSizeMax()) {
|
if (_peer->isMegagroup() && _peer->asChannel()->membersCount() > 0 && _peer->asChannel()->membersCount() <= Global::ChatSizeMax()) {
|
||||||
if (_peer->asChannel()->mgInfo->lastParticipants.size() < _peer->asChannel()->count || _peer->asChannel()->lastParticipantsCountOutdated()) {
|
if (_peer->asChannel()->mgInfo->lastParticipants.size() < _peer->asChannel()->membersCount() || _peer->asChannel()->lastParticipantsCountOutdated()) {
|
||||||
if (App::api()) App::api()->requestLastParticipants(_peer->asChannel());
|
if (App::api()) App::api()->requestLastParticipants(_peer->asChannel());
|
||||||
}
|
}
|
||||||
int32 onlineCount = 0;
|
int32 onlineCount = 0;
|
||||||
|
@ -5852,12 +5872,12 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (onlineCount && !onlyMe) {
|
if (onlineCount && !onlyMe) {
|
||||||
text = lng_chat_status_members_online(lt_count, _peer->asChannel()->count, lt_count_online, onlineCount);
|
text = lng_chat_status_members_online(lt_count, _peer->asChannel()->membersCount(), lt_count_online, onlineCount);
|
||||||
} else {
|
} else {
|
||||||
text = lng_chat_status_members(lt_count, _peer->asChannel()->count);
|
text = lng_chat_status_members(lt_count, _peer->asChannel()->membersCount());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
text = _peer->asChannel()->count ? lng_chat_status_members(lt_count, _peer->asChannel()->count) : lang(_peer->isMegagroup() ? lng_group_status : lng_channel_status);
|
text = _peer->asChannel()->membersCount() ? lng_chat_status_members(lt_count, _peer->asChannel()->membersCount()) : lang(_peer->isMegagroup() ? lng_group_status : lng_channel_status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_titlePeerText != text) {
|
if (_titlePeerText != text) {
|
||||||
|
@ -6494,8 +6514,6 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
|
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
|
||||||
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
|
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
|
||||||
_sideShadow.resize(st::lineWidth, height());
|
|
||||||
_sideShadow.moveToLeft(0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::itemRemoved(HistoryItem *item) {
|
void HistoryWidget::itemRemoved(HistoryItem *item) {
|
||||||
|
@ -6671,7 +6689,8 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
if (toY > _scroll.scrollTopMax()) toY = _scroll.scrollTopMax();
|
auto scrollMax = _scroll.scrollTopMax();
|
||||||
|
accumulate_min(toY, scrollMax);
|
||||||
if (_scroll.scrollTop() == toY) {
|
if (_scroll.scrollTop() == toY) {
|
||||||
visibleAreaUpdated();
|
visibleAreaUpdated();
|
||||||
} else {
|
} else {
|
||||||
|
@ -7041,7 +7060,6 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
|
||||||
}
|
}
|
||||||
connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide()));
|
connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide()));
|
||||||
_reportSpamPanel.raise();
|
_reportSpamPanel.raise();
|
||||||
_sideShadow.raise();
|
|
||||||
_topShadow.raise();
|
_topShadow.raise();
|
||||||
updatePinnedBar();
|
updatePinnedBar();
|
||||||
result = true;
|
result = true;
|
||||||
|
@ -7679,7 +7697,7 @@ void HistoryWidget::peerUpdated(PeerData *data) {
|
||||||
if (App::api()) {
|
if (App::api()) {
|
||||||
if (data->isChat() && data->asChat()->noParticipantInfo()) {
|
if (data->isChat() && data->asChat()->noParticipantInfo()) {
|
||||||
App::api()->requestFullPeer(data);
|
App::api()->requestFullPeer(data);
|
||||||
} else if (data->isUser() && data->asUser()->blocked == UserBlockUnknown) {
|
} else if (data->isUser() && data->asUser()->blockStatus() == UserData::BlockStatus::Unknown) {
|
||||||
App::api()->requestFullPeer(data);
|
App::api()->requestFullPeer(data);
|
||||||
} else if (data->isMegagroup() && !data->asChannel()->mgInfo->botStatus) {
|
} else if (data->isMegagroup() && !data->asChannel()->mgInfo->botStatus) {
|
||||||
App::api()->requestBots(data->asChannel());
|
App::api()->requestBots(data->asChannel());
|
||||||
|
@ -7723,6 +7741,7 @@ void HistoryWidget::onDeleteSelected() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onDeleteSelectedSure() {
|
void HistoryWidget::onDeleteSelectedSure() {
|
||||||
|
Ui::hideLayer();
|
||||||
if (!_list) return;
|
if (!_list) return;
|
||||||
|
|
||||||
SelectedItemSet sel;
|
SelectedItemSet sel;
|
||||||
|
@ -7740,7 +7759,6 @@ void HistoryWidget::onDeleteSelectedSure() {
|
||||||
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
||||||
i.value()->destroy();
|
i.value()->destroy();
|
||||||
}
|
}
|
||||||
Ui::hideLayer();
|
|
||||||
|
|
||||||
for (QMap<PeerData*, QVector<MTPint> >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
|
for (QMap<PeerData*, QVector<MTPint> >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
|
||||||
App::main()->deleteMessages(i.key(), i.value());
|
App::main()->deleteMessages(i.key(), i.value());
|
||||||
|
@ -7748,6 +7766,8 @@ void HistoryWidget::onDeleteSelectedSure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryWidget::onDeleteContextSure() {
|
void HistoryWidget::onDeleteContextSure() {
|
||||||
|
Ui::hideLayer();
|
||||||
|
|
||||||
HistoryItem *item = App::contextItem();
|
HistoryItem *item = App::contextItem();
|
||||||
if (!item || item->type() != HistoryItemMsg) {
|
if (!item || item->type() != HistoryItemMsg) {
|
||||||
return;
|
return;
|
||||||
|
@ -7757,12 +7777,11 @@ void HistoryWidget::onDeleteContextSure() {
|
||||||
History *h = item->history();
|
History *h = item->history();
|
||||||
bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item);
|
bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item);
|
||||||
item->destroy();
|
item->destroy();
|
||||||
|
|
||||||
if (!wasOnServer && wasLast && !h->lastMsg) {
|
if (!wasOnServer && wasLast && !h->lastMsg) {
|
||||||
App::main()->checkPeerHistory(h->peer);
|
App::main()->checkPeerHistory(h->peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ui::hideLayer();
|
|
||||||
|
|
||||||
if (wasOnServer) {
|
if (wasOnServer) {
|
||||||
App::main()->deleteMessages(h->peer, toDelete);
|
App::main()->deleteMessages(h->peer, toDelete);
|
||||||
}
|
}
|
||||||
|
@ -8117,20 +8136,23 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||||
if (r != rect()) {
|
if (r != rect()) {
|
||||||
p.setClipRect(r);
|
p.setClipRect(r);
|
||||||
}
|
}
|
||||||
|
bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
|
||||||
|
|
||||||
if (_a_show.animating()) {
|
if (_a_show.animating()) {
|
||||||
|
int retina = cIntRetinaFactor();
|
||||||
|
int inCacheTop = hasTopBar ? st::topBarHeight : 0;
|
||||||
if (a_coordOver.current() > 0) {
|
if (a_coordOver.current() > 0) {
|
||||||
p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor()));
|
p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * retina, inCacheTop * retina, a_coordOver.current() * retina, height() * retina));
|
||||||
p.setOpacity(a_shadow.current() * st::slideFadeOut);
|
p.setOpacity(a_progress.current() * st::slideFadeOut);
|
||||||
p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b);
|
p.fillRect(0, 0, a_coordOver.current(), height(), st::black);
|
||||||
p.setOpacity(1);
|
p.setOpacity(1);
|
||||||
}
|
}
|
||||||
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
|
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina));
|
||||||
p.setOpacity(a_shadow.current());
|
p.setOpacity(a_progress.current());
|
||||||
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
|
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
|
|
||||||
QRect fill(0, 0, width(), App::main()->height());
|
QRect fill(0, 0, width(), App::main()->height());
|
||||||
int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0;
|
int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0;
|
||||||
QPixmap cached = App::main()->cachedBackground(fill, x, y);
|
QPixmap cached = App::main()->cachedBackground(fill, x, y);
|
||||||
|
@ -8208,8 +8230,7 @@ QStringList HistoryWidget::getMediasFromMime(const QMimeData *d) {
|
||||||
for (QList<QUrl>::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) {
|
for (QList<QUrl>::const_iterator i = urls.cbegin(), en = urls.cend(); i != en; ++i) {
|
||||||
if (!i->isLocalFile()) return QStringList();
|
if (!i->isLocalFile()) return QStringList();
|
||||||
|
|
||||||
QString file(i->toLocalFile());
|
auto file = psConvertFileUrl(*i);
|
||||||
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
|
|
||||||
|
|
||||||
QFileInfo info(file);
|
QFileInfo info(file);
|
||||||
uint64 s = info.size();
|
uint64 s = info.size();
|
||||||
|
|
|
@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "dropdown.h"
|
#include "dropdown.h"
|
||||||
#include "history/history_common.h"
|
#include "history/history_common.h"
|
||||||
#include "history/field_autocomplete.h"
|
#include "history/field_autocomplete.h"
|
||||||
|
#include "window/section_widget.h"
|
||||||
|
|
||||||
namespace InlineBots {
|
namespace InlineBots {
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
@ -205,7 +206,7 @@ private:
|
||||||
Selecting = 0x04,
|
Selecting = 0x04,
|
||||||
};
|
};
|
||||||
DragAction _dragAction = NoDrag;
|
DragAction _dragAction = NoDrag;
|
||||||
TextSelectType _dragSelType = TextSelectLetters;
|
TextSelectType _dragSelType = TextSelectType::Letters;
|
||||||
QPoint _dragStartPos, _dragPos;
|
QPoint _dragStartPos, _dragPos;
|
||||||
HistoryItem *_dragItem = nullptr;
|
HistoryItem *_dragItem = nullptr;
|
||||||
HistoryCursorState _dragCursorState = HistoryDefaultCursorState;
|
HistoryCursorState _dragCursorState = HistoryDefaultCursorState;
|
||||||
|
@ -570,7 +571,10 @@ public:
|
||||||
void setMsgId(MsgId showAtMsgId);
|
void setMsgId(MsgId showAtMsgId);
|
||||||
MsgId msgId() const;
|
MsgId msgId() const;
|
||||||
|
|
||||||
void animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false);
|
bool hasTopBarShadow() const {
|
||||||
|
return peer() != nullptr;
|
||||||
|
}
|
||||||
|
void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms);
|
||||||
void step_show(float64 ms, bool timer);
|
void step_show(float64 ms, bool timer);
|
||||||
void animStop();
|
void animStop();
|
||||||
|
|
||||||
|
@ -652,14 +656,17 @@ public:
|
||||||
bool contentOverlapped(const QRect &globalRect);
|
bool contentOverlapped(const QRect &globalRect);
|
||||||
|
|
||||||
void grabStart() override {
|
void grabStart() override {
|
||||||
_sideShadow.hide();
|
|
||||||
_inGrab = true;
|
_inGrab = true;
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
}
|
}
|
||||||
|
void grapWithoutTopBarShadow() {
|
||||||
|
grabStart();
|
||||||
|
_topShadow.hide();
|
||||||
|
}
|
||||||
void grabFinish() override {
|
void grabFinish() override {
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
_inGrab = false;
|
_inGrab = false;
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
|
_topShadow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isItemVisible(HistoryItem *item);
|
bool isItemVisible(HistoryItem *item);
|
||||||
|
@ -1077,9 +1084,9 @@ private:
|
||||||
int32 _titlePeerTextWidth = 0;
|
int32 _titlePeerTextWidth = 0;
|
||||||
|
|
||||||
Animation _a_show;
|
Animation _a_show;
|
||||||
QPixmap _cacheUnder, _cacheOver, _cacheTopBarUnder, _cacheTopBarOver;
|
QPixmap _cacheUnder, _cacheOver;
|
||||||
anim::ivalue a_coordUnder, a_coordOver;
|
anim::ivalue a_coordUnder, a_coordOver;
|
||||||
anim::fvalue a_shadow;
|
anim::fvalue a_progress;
|
||||||
|
|
||||||
QTimer _scrollTimer;
|
QTimer _scrollTimer;
|
||||||
int32 _scrollDelta = 0;
|
int32 _scrollDelta = 0;
|
||||||
|
@ -1094,7 +1101,7 @@ private:
|
||||||
bool _saveDraftText = false;
|
bool _saveDraftText = false;
|
||||||
QTimer _saveDraftTimer, _saveCloudDraftTimer;
|
QTimer _saveDraftTimer, _saveCloudDraftTimer;
|
||||||
|
|
||||||
PlainShadow _sideShadow, _topShadow;
|
PlainShadow _topShadow;
|
||||||
bool _inGrab = false;
|
bool _inGrab = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -216,9 +216,8 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||||
if (_delete && p == _delete) {
|
if (_delete && p == _delete) {
|
||||||
bool wasactive = (_state & StateFlag::DeleteOver);
|
bool wasactive = (_state & StateFlag::DeleteOver);
|
||||||
if (active != wasactive) {
|
if (active != wasactive) {
|
||||||
float64 from = active ? 0 : 1, to = active ? 1 : 0;
|
auto from = active ? 0. : 1., to = active ? 1. : 0.;
|
||||||
EnsureAnimation(_a_deleteOver, from, func(this, &Gif::update));
|
START_ANIMATION(_a_deleteOver, func(this, &Gif::update), from, to, st::stickersRowDuration, anim::linear);
|
||||||
_a_deleteOver.start(to, st::stickersRowDuration);
|
|
||||||
if (active) {
|
if (active) {
|
||||||
_state |= StateFlag::DeleteOver;
|
_state |= StateFlag::DeleteOver;
|
||||||
} else {
|
} else {
|
||||||
|
@ -231,9 +230,8 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||||
if (active != wasactive) {
|
if (active != wasactive) {
|
||||||
if (!getShownDocument()->loaded()) {
|
if (!getShownDocument()->loaded()) {
|
||||||
ensureAnimation();
|
ensureAnimation();
|
||||||
float64 from = active ? 0 : 1, to = active ? 1 : 0;
|
auto from = active ? 0. : 1., to = active ? 1. : 0.;
|
||||||
EnsureAnimation(_animation->_a_over, from, func(this, &Gif::update));
|
START_ANIMATION(_animation->_a_over, func(this, &Gif::update), from, to, st::stickersRowDuration, anim::linear);
|
||||||
_animation->_a_over.start(to, st::stickersRowDuration);
|
|
||||||
}
|
}
|
||||||
if (active) {
|
if (active) {
|
||||||
_state |= StateFlag::Over;
|
_state |= StateFlag::Over;
|
||||||
|
@ -413,9 +411,8 @@ void Sticker::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||||
if (active != _active) {
|
if (active != _active) {
|
||||||
_active = active;
|
_active = active;
|
||||||
|
|
||||||
float64 from = _active ? 0 : 1, to = _active ? 1 : 0;
|
auto from = active ? 0. : 1., to = active ? 1. : 0.;
|
||||||
EnsureAnimation(_a_over, from, func(this, &Sticker::update));
|
START_ANIMATION(_a_over, func(this, &Sticker::update), from, to, st::stickersRowDuration, anim::linear);
|
||||||
_a_over.start(to, st::stickersRowDuration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ItemBase::clickHandlerActiveChanged(p, active);
|
ItemBase::clickHandlerActiveChanged(p, active);
|
||||||
|
|
|
@ -107,9 +107,9 @@ private:
|
||||||
void clipCallback(ClipReaderNotification notification);
|
void clipCallback(ClipReaderNotification notification);
|
||||||
|
|
||||||
struct AnimationData {
|
struct AnimationData {
|
||||||
AnimationData(AnimationCreator creator)
|
AnimationData(AnimationCallbacks &&callbacks)
|
||||||
: over(false)
|
: over(false)
|
||||||
, radial(creator) {
|
, radial(std_::move(callbacks)) {
|
||||||
}
|
}
|
||||||
bool over;
|
bool over;
|
||||||
FloatAnimation _a_over;
|
FloatAnimation _a_over;
|
||||||
|
@ -268,9 +268,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AnimationData {
|
struct AnimationData {
|
||||||
AnimationData(AnimationCreator thumbOverCallbacks, AnimationCreator radialCallbacks) : a_thumbOver(0, 0)
|
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
|
||||||
, _a_thumbOver(thumbOverCallbacks)
|
, _a_thumbOver(std_::move(thumbOverCallbacks))
|
||||||
, radial(radialCallbacks) {
|
, radial(std_::move(radialCallbacks)) {
|
||||||
}
|
}
|
||||||
anim::fvalue a_thumbOver;
|
anim::fvalue a_thumbOver;
|
||||||
Animation _a_thumbOver;
|
Animation _a_thumbOver;
|
||||||
|
|
|
@ -50,7 +50,7 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent)
|
||||||
, country(this, st::introCountry)
|
, country(this, st::introCountry)
|
||||||
, phone(this, st::inpIntroPhone)
|
, phone(this, st::inpIntroPhone)
|
||||||
, code(this, st::inpIntroCountryCode)
|
, code(this, st::inpIntroCountryCode)
|
||||||
, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), st::introErrLabel, st::introErrLabelTextStyle)
|
, _signup(this, lng_phone_notreg(lt_signup_start, textcmdStartLink(1), lt_signup_end, textcmdStopLink()), FlatLabel::InitType::Rich, st::introErrLabel, st::introErrLabelTextStyle)
|
||||||
, _showSignup(false)
|
, _showSignup(false)
|
||||||
, sentRequest(0) {
|
, sentRequest(0) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
|
|
@ -20,9 +20,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtWidgets/QWidget>
|
|
||||||
#include "ui/flatbutton.h"
|
#include "ui/flatbutton.h"
|
||||||
#include "ui/countryinput.h"
|
#include "ui/countryinput.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
#include "intro/introwidget.h"
|
#include "intro/introwidget.h"
|
||||||
|
|
||||||
class IntroPhone final : public IntroStep {
|
class IntroPhone final : public IntroStep {
|
||||||
|
|
|
@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "langloaderplain.h"
|
#include "langloaderplain.h"
|
||||||
|
|
||||||
IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent)
|
IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent)
|
||||||
, _intro(this, lang(lng_intro), st::introLabel, st::introLabelTextStyle)
|
, _intro(this, lang(lng_intro), FlatLabel::InitType::Rich, st::introLabel, st::introLabelTextStyle)
|
||||||
, _changeLang(this, QString())
|
, _changeLang(this, QString())
|
||||||
, _next(this, lang(lng_start_msgs), st::btnIntroNext) {
|
, _next(this, lang(lng_start_msgs), st::btnIntroNext) {
|
||||||
_changeLang.hide();
|
_changeLang.hide();
|
||||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "intro/introwidget.h"
|
#include "intro/introwidget.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
|
|
||||||
class IntroStart final : public IntroStep {
|
class IntroStart final : public IntroStep {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -180,7 +180,7 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) {
|
||||||
step()->hide();
|
step()->hide();
|
||||||
_back.hide();
|
_back.hide();
|
||||||
|
|
||||||
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
|
a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift);
|
||||||
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
||||||
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
||||||
_a_show.start();
|
_a_show.start();
|
||||||
|
|
|
@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "serialize/serialize_document.h"
|
#include "serialize/serialize_document.h"
|
||||||
#include "serialize/serialize_common.h"
|
#include "serialize/serialize_common.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
|
@ -3412,7 +3413,7 @@ namespace Local {
|
||||||
UserData *user = peer->asUser();
|
UserData *user = peer->asUser();
|
||||||
|
|
||||||
// first + last + phone + username + access
|
// first + last + phone + username + access
|
||||||
result += Serialize::stringSize(user->firstName) + Serialize::stringSize(user->lastName) + Serialize::stringSize(user->phone) + Serialize::stringSize(user->username) + sizeof(quint64);
|
result += Serialize::stringSize(user->firstName) + Serialize::stringSize(user->lastName) + Serialize::stringSize(user->phone()) + Serialize::stringSize(user->username) + sizeof(quint64);
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
if (AppVersion >= 9012) {
|
if (AppVersion >= 9012) {
|
||||||
|
@ -3424,13 +3425,13 @@ namespace Local {
|
||||||
} else if (peer->isChat()) {
|
} else if (peer->isChat()) {
|
||||||
ChatData *chat = peer->asChat();
|
ChatData *chat = peer->asChat();
|
||||||
|
|
||||||
// name + count + date + version + admin + forbidden + left + invitationUrl
|
// name + count + date + version + admin + forbidden + left + inviteLink
|
||||||
result += Serialize::stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(chat->invitationUrl);
|
result += Serialize::stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(chat->inviteLink());
|
||||||
} else if (peer->isChannel()) {
|
} else if (peer->isChannel()) {
|
||||||
ChannelData *channel = peer->asChannel();
|
ChannelData *channel = peer->asChannel();
|
||||||
|
|
||||||
// name + access + date + version + forbidden + flags + invitationUrl
|
// name + access + date + version + forbidden + flags + inviteLink
|
||||||
result += Serialize::stringSize(channel->name) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(channel->invitationUrl);
|
result += Serialize::stringSize(channel->name) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(channel->inviteLink());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -3441,7 +3442,7 @@ namespace Local {
|
||||||
if (peer->isUser()) {
|
if (peer->isUser()) {
|
||||||
UserData *user = peer->asUser();
|
UserData *user = peer->asUser();
|
||||||
|
|
||||||
stream << user->firstName << user->lastName << user->phone << user->username << quint64(user->access);
|
stream << user->firstName << user->lastName << user->phone() << user->username << quint64(user->access);
|
||||||
if (AppVersion >= 9012) {
|
if (AppVersion >= 9012) {
|
||||||
stream << qint32(user->flags);
|
stream << qint32(user->flags);
|
||||||
}
|
}
|
||||||
|
@ -3455,12 +3456,12 @@ namespace Local {
|
||||||
qint32 flagsData = (AppVersion >= 9012) ? chat->flags : (chat->haveLeft() ? 1 : 0);
|
qint32 flagsData = (AppVersion >= 9012) ? chat->flags : (chat->haveLeft() ? 1 : 0);
|
||||||
|
|
||||||
stream << chat->name << qint32(chat->count) << qint32(chat->date) << qint32(chat->version) << qint32(chat->creator);
|
stream << chat->name << qint32(chat->count) << qint32(chat->date) << qint32(chat->version) << qint32(chat->creator);
|
||||||
stream << qint32(chat->isForbidden ? 1 : 0) << qint32(flagsData) << chat->invitationUrl;
|
stream << qint32(chat->isForbidden ? 1 : 0) << qint32(flagsData) << chat->inviteLink();
|
||||||
} else if (peer->isChannel()) {
|
} else if (peer->isChannel()) {
|
||||||
ChannelData *channel = peer->asChannel();
|
ChannelData *channel = peer->asChannel();
|
||||||
|
|
||||||
stream << channel->name << quint64(channel->access) << qint32(channel->date) << qint32(channel->version);
|
stream << channel->name << quint64(channel->access) << qint32(channel->date) << qint32(channel->version);
|
||||||
stream << qint32(channel->isForbidden ? 1 : 0) << qint32(channel->flags) << channel->invitationUrl;
|
stream << qint32(channel->isForbidden ? 1 : 0) << qint32(channel->flags) << channel->inviteLink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3495,6 +3496,7 @@ namespace Local {
|
||||||
QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString();
|
QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString();
|
||||||
|
|
||||||
if (!wasLoaded) {
|
if (!wasLoaded) {
|
||||||
|
user->setPhone(phone);
|
||||||
user->setName(first, last, pname, username);
|
user->setName(first, last, pname, username);
|
||||||
|
|
||||||
user->access = access;
|
user->access = access;
|
||||||
|
@ -3519,9 +3521,9 @@ namespace Local {
|
||||||
} else if (result->isChat()) {
|
} else if (result->isChat()) {
|
||||||
ChatData *chat = result->asChat();
|
ChatData *chat = result->asChat();
|
||||||
|
|
||||||
QString name, invitationUrl;
|
QString name, inviteLink;
|
||||||
qint32 count, date, version, creator, forbidden, flagsData, flags;
|
qint32 count, date, version, creator, forbidden, flagsData, flags;
|
||||||
from.stream >> name >> count >> date >> version >> creator >> forbidden >> flagsData >> invitationUrl;
|
from.stream >> name >> count >> date >> version >> creator >> forbidden >> flagsData >> inviteLink;
|
||||||
|
|
||||||
if (from.version >= 9012) {
|
if (from.version >= 9012) {
|
||||||
flags = flagsData;
|
flags = flagsData;
|
||||||
|
@ -3530,14 +3532,14 @@ namespace Local {
|
||||||
flags = (flagsData == 1) ? MTPDchat::Flags(MTPDchat::Flag::f_left) : MTPDchat::Flags(0);
|
flags = (flagsData == 1) ? MTPDchat::Flags(MTPDchat::Flag::f_left) : MTPDchat::Flags(0);
|
||||||
}
|
}
|
||||||
if (!wasLoaded) {
|
if (!wasLoaded) {
|
||||||
chat->updateName(name, QString(), QString());
|
chat->setName(name);
|
||||||
chat->count = count;
|
chat->count = count;
|
||||||
chat->date = date;
|
chat->date = date;
|
||||||
chat->version = version;
|
chat->version = version;
|
||||||
chat->creator = creator;
|
chat->creator = creator;
|
||||||
chat->isForbidden = (forbidden == 1);
|
chat->isForbidden = (forbidden == 1);
|
||||||
chat->flags = MTPDchat::Flags(flags);
|
chat->flags = MTPDchat::Flags(flags);
|
||||||
chat->invitationUrl = invitationUrl;
|
chat->setInviteLink(inviteLink);
|
||||||
|
|
||||||
chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id)));
|
chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id)));
|
||||||
chat->inputChat = MTP_int(peerToChat(chat->id));
|
chat->inputChat = MTP_int(peerToChat(chat->id));
|
||||||
|
@ -3547,19 +3549,19 @@ namespace Local {
|
||||||
} else if (result->isChannel()) {
|
} else if (result->isChannel()) {
|
||||||
ChannelData *channel = result->asChannel();
|
ChannelData *channel = result->asChannel();
|
||||||
|
|
||||||
QString name, invitationUrl;
|
QString name, inviteLink;
|
||||||
quint64 access;
|
quint64 access;
|
||||||
qint32 date, version, forbidden, flags;
|
qint32 date, version, forbidden, flags;
|
||||||
from.stream >> name >> access >> date >> version >> forbidden >> flags >> invitationUrl;
|
from.stream >> name >> access >> date >> version >> forbidden >> flags >> inviteLink;
|
||||||
|
|
||||||
if (!wasLoaded) {
|
if (!wasLoaded) {
|
||||||
channel->updateName(name, QString(), QString());
|
channel->setName(name, QString());
|
||||||
channel->access = access;
|
channel->access = access;
|
||||||
channel->date = date;
|
channel->date = date;
|
||||||
channel->version = version;
|
channel->version = version;
|
||||||
channel->isForbidden = (forbidden == 1);
|
channel->isForbidden = (forbidden == 1);
|
||||||
channel->flags = MTPDchannel::Flags(flags);
|
channel->flags = MTPDchannel::Flags(flags);
|
||||||
channel->invitationUrl = invitationUrl;
|
channel->setInviteLink(inviteLink);
|
||||||
|
|
||||||
channel->input = MTP_inputPeerChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access));
|
channel->input = MTP_inputPeerChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access));
|
||||||
channel->inputChannel = MTP_inputChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access));
|
channel->inputChannel = MTP_inputChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access));
|
||||||
|
@ -3749,7 +3751,7 @@ namespace Local {
|
||||||
cRefSavedPeersByTime().insert(t, peer);
|
cRefSavedPeersByTime().insert(t, peer);
|
||||||
peers.push_back(peer);
|
peers.push_back(peer);
|
||||||
}
|
}
|
||||||
App::emitPeerUpdated();
|
|
||||||
if (App::api()) App::api()->requestPeers(peers);
|
if (App::api()) App::api()->requestPeers(peers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ class PeerAvatarButton;
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class TopBarWidget;
|
class TopBarWidget;
|
||||||
|
class SectionMemento;
|
||||||
|
class SectionWidget;
|
||||||
|
struct SectionSlideParams;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
|
@ -40,7 +43,6 @@ class ApiWrap;
|
||||||
class ConfirmBox;
|
class ConfirmBox;
|
||||||
class DialogsWidget;
|
class DialogsWidget;
|
||||||
class HistoryWidget;
|
class HistoryWidget;
|
||||||
class ProfileWidget;
|
|
||||||
class OverviewWidget;
|
class OverviewWidget;
|
||||||
class PlayerWidget;
|
class PlayerWidget;
|
||||||
class HistoryHider;
|
class HistoryHider;
|
||||||
|
@ -48,7 +50,7 @@ class Dropdown;
|
||||||
|
|
||||||
enum StackItemType {
|
enum StackItemType {
|
||||||
HistoryStackItem,
|
HistoryStackItem,
|
||||||
ProfileStackItem,
|
SectionStackItem,
|
||||||
OverviewStackItem,
|
OverviewStackItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,8 +66,9 @@ public:
|
||||||
|
|
||||||
class StackItemHistory : public StackItem {
|
class StackItemHistory : public StackItem {
|
||||||
public:
|
public:
|
||||||
StackItemHistory(PeerData *peer, MsgId msgId, QList<MsgId> replyReturns) : StackItem(peer),
|
StackItemHistory(PeerData *peer, MsgId msgId, QList<MsgId> replyReturns) : StackItem(peer)
|
||||||
msgId(msgId), replyReturns(replyReturns) {
|
, msgId(msgId)
|
||||||
|
, replyReturns(replyReturns) {
|
||||||
}
|
}
|
||||||
StackItemType type() const {
|
StackItemType type() const {
|
||||||
return HistoryStackItem;
|
return HistoryStackItem;
|
||||||
|
@ -74,19 +77,29 @@ msgId(msgId), replyReturns(replyReturns) {
|
||||||
QList<MsgId> replyReturns;
|
QList<MsgId> replyReturns;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StackItemProfile : public StackItem {
|
class StackItemSection : public StackItem {
|
||||||
public:
|
public:
|
||||||
StackItemProfile(PeerData *peer, int32 lastScrollTop) : StackItem(peer), lastScrollTop(lastScrollTop) {
|
StackItemSection(std_::unique_ptr<Window::SectionMemento> &&memento);
|
||||||
}
|
~StackItemSection();
|
||||||
|
|
||||||
StackItemType type() const {
|
StackItemType type() const {
|
||||||
return ProfileStackItem;
|
return SectionStackItem;
|
||||||
}
|
}
|
||||||
int32 lastScrollTop;
|
Window::SectionMemento *memento() const {
|
||||||
|
return _memento.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std_::unique_ptr<Window::SectionMemento> _memento;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class StackItemOverview : public StackItem {
|
class StackItemOverview : public StackItem {
|
||||||
public:
|
public:
|
||||||
StackItemOverview(PeerData *peer, MediaOverviewType mediaType, int32 lastWidth, int32 lastScrollTop) : StackItem(peer), mediaType(mediaType), lastWidth(lastWidth), lastScrollTop(lastScrollTop) {
|
StackItemOverview(PeerData *peer, MediaOverviewType mediaType, int32 lastWidth, int32 lastScrollTop) : StackItem(peer)
|
||||||
|
, mediaType(mediaType)
|
||||||
|
, lastWidth(lastWidth)
|
||||||
|
, lastScrollTop(lastScrollTop) {
|
||||||
}
|
}
|
||||||
StackItemType type() const {
|
StackItemType type() const {
|
||||||
return OverviewStackItem;
|
return OverviewStackItem;
|
||||||
|
@ -173,8 +186,6 @@ public:
|
||||||
void startFull(const MTPVector<MTPUser> &users);
|
void startFull(const MTPVector<MTPUser> &users);
|
||||||
bool started();
|
bool started();
|
||||||
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
|
void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0);
|
||||||
void gotNotifySetting(MTPInputNotifyPeer peer, const MTPPeerNotifySettings &settings);
|
|
||||||
bool failNotifySetting(MTPInputNotifyPeer peer, const RPCError &error);
|
|
||||||
|
|
||||||
void updateNotifySetting(PeerData *peer, NotifySettingStatus notify, SilentNotifiesStatus silent = SilentNotifiesDontChange);
|
void updateNotifySetting(PeerData *peer, NotifySettingStatus notify, SilentNotifiesStatus silent = SilentNotifiesDontChange);
|
||||||
|
|
||||||
|
@ -210,14 +221,14 @@ public:
|
||||||
PeerData *activePeer();
|
PeerData *activePeer();
|
||||||
MsgId activeMsgId();
|
MsgId activeMsgId();
|
||||||
|
|
||||||
PeerData *profilePeer();
|
|
||||||
PeerData *overviewPeer();
|
PeerData *overviewPeer();
|
||||||
bool mediaTypeSwitch();
|
bool mediaTypeSwitch();
|
||||||
void showPeerProfile(PeerData *peer, bool back = false, int32 lastScrollTop = -1);
|
void showWideSection(const Window::SectionMemento &memento);
|
||||||
void showMediaOverview(PeerData *peer, MediaOverviewType type, bool back = false, int32 lastScrollTop = -1);
|
void showMediaOverview(PeerData *peer, MediaOverviewType type, bool back = false, int32 lastScrollTop = -1);
|
||||||
void showBackFromStack();
|
void showBackFromStack();
|
||||||
void orderWidgets();
|
void orderWidgets();
|
||||||
QRect historyRect() const;
|
QRect historyRect() const;
|
||||||
|
QPixmap grabForShowAnimation(const Window::SectionSlideParams ¶ms);
|
||||||
|
|
||||||
void onSendFileConfirm(const FileLoadResultPtr &file, bool ctrlShiftEnter);
|
void onSendFileConfirm(const FileLoadResultPtr &file, bool ctrlShiftEnter);
|
||||||
void onSendFileCancel(const FileLoadResultPtr &file);
|
void onSendFileCancel(const FileLoadResultPtr &file);
|
||||||
|
@ -262,6 +273,7 @@ public:
|
||||||
void deleteMessages(PeerData *peer, const QVector<MTPint> &ids);
|
void deleteMessages(PeerData *peer, const QVector<MTPint> &ids);
|
||||||
void deletedContact(UserData *user, const MTPcontacts_Link &result);
|
void deletedContact(UserData *user, const MTPcontacts_Link &result);
|
||||||
void deleteConversation(PeerData *peer, bool deleteHistory = true);
|
void deleteConversation(PeerData *peer, bool deleteHistory = true);
|
||||||
|
void deleteAndExit(ChatData *chat);
|
||||||
void clearHistory(PeerData *peer);
|
void clearHistory(PeerData *peer);
|
||||||
void deleteAllFromUser(ChannelData *channel, UserData *from);
|
void deleteAllFromUser(ChannelData *channel, UserData *from);
|
||||||
|
|
||||||
|
@ -313,7 +325,6 @@ public:
|
||||||
void itemEdited(HistoryItem *item);
|
void itemEdited(HistoryItem *item);
|
||||||
|
|
||||||
void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
|
void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
|
||||||
void peerUsernameChanged(PeerData *peer);
|
|
||||||
|
|
||||||
void checkLastUpdate(bool afterSleep);
|
void checkLastUpdate(bool afterSleep);
|
||||||
void showAddContact();
|
void showAddContact();
|
||||||
|
@ -483,10 +494,9 @@ public slots:
|
||||||
void ui_autoplayMediaInlineAsync(qint32 channelId, qint32 msgId);
|
void ui_autoplayMediaInlineAsync(qint32 channelId, qint32 msgId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void sendReadRequest(PeerData *peer, MsgId upTo);
|
void sendReadRequest(PeerData *peer, MsgId upTo);
|
||||||
void channelReadDone(PeerData *peer, const MTPBool &result);
|
void channelReadDone(PeerData *peer, const MTPBool &result);
|
||||||
void historyReadDone(PeerData *peer, const MTPmessages_AffectedMessages &result);
|
void historyReadDone(PeerData *peer, const MTPmessages_AffectedMessages &result);
|
||||||
bool readRequestFail(PeerData *peer, const RPCError &error);
|
bool readRequestFail(PeerData *peer, const RPCError &error);
|
||||||
void readRequestDone(PeerData *peer);
|
void readRequestDone(PeerData *peer);
|
||||||
|
|
||||||
|
@ -496,6 +506,15 @@ private:
|
||||||
void saveCloudDraftDone(PeerData *peer, const MTPBool &result, mtpRequestId requestId);
|
void saveCloudDraftDone(PeerData *peer, const MTPBool &result, mtpRequestId requestId);
|
||||||
bool saveCloudDraftFail(PeerData *peer, const RPCError &error, mtpRequestId requestId);
|
bool saveCloudDraftFail(PeerData *peer, const RPCError &error, mtpRequestId requestId);
|
||||||
|
|
||||||
|
Window::SectionSlideParams prepareShowAnimation(bool willHaveTopBarShadow);
|
||||||
|
void showWideSectionAnimated(const Window::SectionMemento *memento, bool back);
|
||||||
|
|
||||||
|
// All this methods use the prepareShowAnimation().
|
||||||
|
Window::SectionSlideParams prepareWideSectionAnimation(Window::SectionWidget *section);
|
||||||
|
Window::SectionSlideParams prepareHistoryAnimation(PeerId historyPeerId);
|
||||||
|
Window::SectionSlideParams prepareOverviewAnimation();
|
||||||
|
Window::SectionSlideParams prepareDialogsAnimation();
|
||||||
|
|
||||||
bool _started = false;
|
bool _started = false;
|
||||||
|
|
||||||
uint64 failedObjId = 0;
|
uint64 failedObjId = 0;
|
||||||
|
@ -565,9 +584,11 @@ private:
|
||||||
|
|
||||||
int _dialogsWidth = st::dlgMinWidth;
|
int _dialogsWidth = st::dlgMinWidth;
|
||||||
|
|
||||||
|
PlainShadow _sideShadow;
|
||||||
|
|
||||||
ChildWidget<DialogsWidget> _dialogs;
|
ChildWidget<DialogsWidget> _dialogs;
|
||||||
ChildWidget<HistoryWidget> _history;
|
ChildWidget<HistoryWidget> _history;
|
||||||
ChildWidget<ProfileWidget> _profile = { nullptr };
|
ChildWidget<Window::SectionWidget> _wideSection = { nullptr };
|
||||||
ChildWidget<OverviewWidget> _overview = { nullptr };
|
ChildWidget<OverviewWidget> _overview = { nullptr };
|
||||||
ChildWidget<PlayerWidget> _player;
|
ChildWidget<PlayerWidget> _player;
|
||||||
ChildWidget<Window::TopBarWidget> _topBar;
|
ChildWidget<Window::TopBarWidget> _topBar;
|
||||||
|
|
|
@ -35,9 +35,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
#include "boxes/confirmbox.h"
|
#include "boxes/confirmbox.h"
|
||||||
#include "boxes/contactsbox.h"
|
#include "boxes/contactsbox.h"
|
||||||
#include "boxes/addcontactbox.h"
|
#include "boxes/addcontactbox.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
#include "autoupdater.h"
|
#include "autoupdater.h"
|
||||||
#include "mediaview.h"
|
#include "mediaview.h"
|
||||||
#include "localstorage.h"
|
#include "localstorage.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
|
||||||
ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : QWidget(parent), _shadow(st::boxShadow), _reconnect(this, QString()) {
|
ConnectingWidget::ConnectingWidget(QWidget *parent, const QString &text, const QString &reconnect) : QWidget(parent), _shadow(st::boxShadow), _reconnect(this, QString()) {
|
||||||
set(text, reconnect);
|
set(text, reconnect);
|
||||||
|
@ -435,7 +437,7 @@ void MainWindow::init() {
|
||||||
connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(checkHistoryActivation()), Qt::QueuedConnection);
|
connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(checkHistoryActivation()), Qt::QueuedConnection);
|
||||||
|
|
||||||
QPalette p(palette());
|
QPalette p(palette());
|
||||||
p.setColor(QPalette::Window, st::wndBG->c);
|
p.setColor(QPalette::Window, st::windowBg->c);
|
||||||
setPalette(p);
|
setPalette(p);
|
||||||
|
|
||||||
title = new TitleWidget(this);
|
title = new TitleWidget(this);
|
||||||
|
@ -613,10 +615,6 @@ void MainWindow::setupIntro(bool anim) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::getNotifySetting(const MTPInputNotifyPeer &peer, uint32 msWait) {
|
|
||||||
MTP::send(MTPaccount_GetNotifySettings(peer), main->rpcDone(&MainWidget::gotNotifySetting, peer), main->rpcFail(&MainWidget::failNotifySetting, peer), 0, msWait);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::serviceNotification(const QString &msg, const MTPMessageMedia &media, bool force) {
|
void MainWindow::serviceNotification(const QString &msg, const MTPMessageMedia &media, bool force) {
|
||||||
History *h = (main && App::userLoaded(ServiceUserId)) ? App::history(ServiceUserId) : 0;
|
History *h = (main && App::userLoaded(ServiceUserId)) ? App::history(ServiceUserId) : 0;
|
||||||
if (!h || (!force && h->isEmpty())) {
|
if (!h || (!force && h->isEmpty())) {
|
||||||
|
@ -1374,7 +1372,7 @@ void MainWindow::onClearFailed(int task, void *manager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::notifySchedule(History *history, HistoryItem *item) {
|
void MainWindow::notifySchedule(History *history, HistoryItem *item) {
|
||||||
if (App::quitting() || !history->currentNotification() || !main) return;
|
if (App::quitting() || !history->currentNotification() || !App::api()) return;
|
||||||
|
|
||||||
PeerData *notifyByFrom = (!history->peer->isUser() && item->mentionsMe()) ? item->from() : 0;
|
PeerData *notifyByFrom = (!history->peer->isUser() && item->mentionsMe()) ? item->from() : 0;
|
||||||
|
|
||||||
|
@ -1394,7 +1392,7 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(notifyByFrom->input));
|
App::api()->requestNotifySetting(notifyByFrom);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
history->popNotification(item);
|
history->popNotification(item);
|
||||||
|
@ -1403,9 +1401,9 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (notifyByFrom && notifyByFrom->notify == UnknownNotifySettings) {
|
if (notifyByFrom && notifyByFrom->notify == UnknownNotifySettings) {
|
||||||
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(notifyByFrom->input), 10);
|
App::api()->requestNotifySetting(notifyByFrom);
|
||||||
}
|
}
|
||||||
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(history->peer->input));
|
App::api()->requestNotifySetting(history->peer);
|
||||||
}
|
}
|
||||||
if (!item->notificationReady()) {
|
if (!item->notificationReady()) {
|
||||||
haveSetting = false;
|
haveSetting = false;
|
||||||
|
@ -1420,6 +1418,7 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) {
|
||||||
} else if (cOtherOnline() >= t) {
|
} else if (cOtherOnline() >= t) {
|
||||||
delay = Global::NotifyDefaultDelay();
|
delay = Global::NotifyDefaultDelay();
|
||||||
}
|
}
|
||||||
|
// LOG(("Is online: %1, otherOnline: %2, currentTime: %3, otherNotOld: %4, otherLaterThanMe: %5").arg(Logs::b(isOnline)).arg(cOtherOnline()).arg(t).arg(Logs::b(otherNotOld)).arg(Logs::b(otherLaterThanMe)));
|
||||||
|
|
||||||
uint64 when = ms + delay;
|
uint64 when = ms + delay;
|
||||||
notifyWhenAlerts[history].insert(when, notifyByFrom);
|
notifyWhenAlerts[history].insert(when, notifyByFrom);
|
||||||
|
@ -1882,8 +1881,15 @@ void MainWindow::sendPaths() {
|
||||||
|
|
||||||
void MainWindow::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
|
void MainWindow::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
|
||||||
if (main) main->mediaOverviewUpdated(peer, type);
|
if (main) main->mediaOverviewUpdated(peer, type);
|
||||||
if (!_mediaView || _mediaView->isHidden()) return;
|
if (_mediaView && !_mediaView->isHidden()) {
|
||||||
_mediaView->mediaOverviewUpdated(peer, type);
|
_mediaView->mediaOverviewUpdated(peer, type);
|
||||||
|
}
|
||||||
|
if (type != OverviewCount) {
|
||||||
|
Notify::PeerUpdate update(peer);
|
||||||
|
update.flags |= Notify::PeerUpdate::Flag::SharedMediaChanged;
|
||||||
|
update.mediaTypesMask |= (1 << type);
|
||||||
|
Notify::peerUpdatedDelayed(update);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::documentUpdated(DocumentData *doc) {
|
void MainWindow::documentUpdated(DocumentData *doc) {
|
||||||
|
|
|
@ -156,7 +156,6 @@ public:
|
||||||
void checkAutoLockIn(int msec);
|
void checkAutoLockIn(int msec);
|
||||||
void setupIntro(bool anim);
|
void setupIntro(bool anim);
|
||||||
void setupMain(bool anim, const MTPUser *user = 0);
|
void setupMain(bool anim, const MTPUser *user = 0);
|
||||||
void getNotifySetting(const MTPInputNotifyPeer &peer, uint32 msWait = 0);
|
|
||||||
void serviceNotification(const QString &msg, const MTPMessageMedia &media = MTP_messageMediaEmpty(), bool force = false);
|
void serviceNotification(const QString &msg, const MTPMessageMedia &media = MTP_messageMediaEmpty(), bool force = false);
|
||||||
void sendServiceHistoryRequest();
|
void sendServiceHistoryRequest();
|
||||||
void showDelayedServiceMsgs();
|
void showDelayedServiceMsgs();
|
||||||
|
|
|
@ -1875,7 +1875,7 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
if (_over == OverName && _down == OverName) {
|
if (_over == OverName && _down == OverName) {
|
||||||
if (App::wnd() && _from) {
|
if (App::wnd() && _from) {
|
||||||
close();
|
close();
|
||||||
if (App::main()) App::main()->showPeerProfile(_from);
|
Ui::showPeerProfile(_from);
|
||||||
}
|
}
|
||||||
} else if (_over == OverDate && _down == OverDate) {
|
} else if (_over == OverDate && _down == OverDate) {
|
||||||
onToMessage();
|
onToMessage();
|
||||||
|
|
|
@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
#include "mtproto/core_types.h"
|
#include "mtproto/core_types.h"
|
||||||
#include "mtproto/session.h"
|
#include "mtproto/session.h"
|
||||||
#include "mtproto/file_download.h"
|
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
|
|
||||||
|
|
|
@ -208,6 +208,7 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &
|
||||||
}
|
}
|
||||||
emit App::wnd()->imageLoaded();
|
emit App::wnd()->imageLoaded();
|
||||||
emit progress(this);
|
emit progress(this);
|
||||||
|
FileDownload::internal::notifyImageLoaded();
|
||||||
loadNext();
|
loadNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,6 +550,9 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe
|
||||||
if (DebugLogging::FileLoader() && _id) DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests)));
|
if (DebugLogging::FileLoader() && _id) DEBUG_LOG(("FileLoader(%1): not done yet, _lastComplete=%2, _size=%3, _nextRequestOffset=%4, _requests=%5").arg(_id).arg(Logs::b(_lastComplete)).arg(_size).arg(_nextRequestOffset).arg(serializereqs(_requests)));
|
||||||
}
|
}
|
||||||
emit progress(this);
|
emit progress(this);
|
||||||
|
if (_complete) {
|
||||||
|
FileDownload::internal::notifyImageLoaded();
|
||||||
|
}
|
||||||
loadNext();
|
loadNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -678,6 +682,7 @@ void webFileLoader::onFinished(const QByteArray &data) {
|
||||||
Local::writeWebFile(_url, _data);
|
Local::writeWebFile(_url, _data);
|
||||||
}
|
}
|
||||||
emit progress(this);
|
emit progress(this);
|
||||||
|
FileDownload::internal::notifyImageLoaded();
|
||||||
loadNext();
|
loadNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1089,3 +1094,25 @@ namespace MTP {
|
||||||
++GlobalPriority;
|
++GlobalPriority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace FileDownload {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using internal::ImageLoadedHandler;
|
||||||
|
|
||||||
|
Notify::SimpleObservedEventRegistrator<ImageLoadedHandler> creator(nullptr, nullptr);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler) {
|
||||||
|
return creator.registerObserver(std_::forward<ImageLoadedHandler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyImageLoaded() {
|
||||||
|
creator.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
}
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
namespace MTP {
|
namespace MTP {
|
||||||
void clearLoaderPriorities();
|
void clearLoaderPriorities();
|
||||||
}
|
}
|
||||||
|
@ -391,3 +393,21 @@ static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation<WebL
|
||||||
|
|
||||||
void reinitWebLoadManager();
|
void reinitWebLoadManager();
|
||||||
void stopWebLoadManager();
|
void stopWebLoadManager();
|
||||||
|
|
||||||
|
namespace FileDownload {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
using ImageLoadedHandler = Function<void>;
|
||||||
|
Notify::ConnectionId plainRegisterImageLoadedObserver(ImageLoadedHandler &&handler);
|
||||||
|
|
||||||
|
void notifyImageLoaded();
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
void registerImageLoadedObserver(ObserverType *observer, void (ObserverType::*handler)()) {
|
||||||
|
auto connection = internal::plainRegisterImageLoadedObserver(func(observer, handler));
|
||||||
|
Notify::observerRegistered(observer, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileDownload
|
||||||
|
|
124
Telegram/SourceFiles/observer_peer.cpp
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
|
namespace App {
|
||||||
|
// Temp forward declaration (while all peer updates are not done through observers).
|
||||||
|
void emitPeerUpdated();
|
||||||
|
} // namespace App
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using internal::PeerUpdateHandler;
|
||||||
|
|
||||||
|
using SmallUpdatesList = QVector<PeerUpdate>;
|
||||||
|
NeverFreedPointer<SmallUpdatesList> SmallUpdates;
|
||||||
|
using AllUpdatesList = QMap<PeerData*, PeerUpdate>;
|
||||||
|
NeverFreedPointer<AllUpdatesList> AllUpdates;
|
||||||
|
|
||||||
|
void StartCallback() {
|
||||||
|
SmallUpdates.makeIfNull();
|
||||||
|
AllUpdates.makeIfNull();
|
||||||
|
}
|
||||||
|
void FinishCallback() {
|
||||||
|
SmallUpdates.clear();
|
||||||
|
AllUpdates.clear();
|
||||||
|
}
|
||||||
|
ObservedEventRegistrator<PeerUpdate::Flags, PeerUpdateHandler> creator(StartCallback, FinishCallback);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
ConnectionId plainRegisterPeerObserver(PeerUpdate::Flags events, PeerUpdateHandler &&handler) {
|
||||||
|
return creator.registerObserver(events, std_::forward<PeerUpdateHandler>(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
|
||||||
|
if (!(mergeTo.flags & PeerUpdate::Flag::NameChanged)) {
|
||||||
|
if (mergeFrom.flags & PeerUpdate::Flag::NameChanged) {
|
||||||
|
mergeTo.oldNames = mergeFrom.oldNames;
|
||||||
|
mergeTo.oldNameFirstChars = mergeFrom.oldNameFirstChars;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mergeFrom.flags & PeerUpdate::Flag::SharedMediaChanged) {
|
||||||
|
mergeTo.mediaTypesMask |= mergeFrom.mediaTypesMask;
|
||||||
|
}
|
||||||
|
mergeTo.flags |= mergeFrom.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void peerUpdatedDelayed(const PeerUpdate &update) {
|
||||||
|
t_assert(creator.started());
|
||||||
|
|
||||||
|
Global::RefHandleDelayedPeerUpdates().call();
|
||||||
|
|
||||||
|
int existingUpdatesCount = SmallUpdates->size();
|
||||||
|
for (int i = 0; i < existingUpdatesCount; ++i) {
|
||||||
|
auto &existingUpdate = (*SmallUpdates)[i];
|
||||||
|
if (existingUpdate.peer == update.peer) {
|
||||||
|
mergePeerUpdate(existingUpdate, update);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (AllUpdates->isEmpty()) {
|
||||||
|
if (existingUpdatesCount < 5) {
|
||||||
|
SmallUpdates->push_back(update);
|
||||||
|
} else {
|
||||||
|
AllUpdates->insert(update.peer, update);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = AllUpdates->find(update.peer);
|
||||||
|
if (it != AllUpdates->cend()) {
|
||||||
|
mergePeerUpdate(it.value(), update);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AllUpdates->insert(update.peer, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void peerUpdatedSendDelayed() {
|
||||||
|
if (!creator.started()) return;
|
||||||
|
|
||||||
|
App::emitPeerUpdated();
|
||||||
|
|
||||||
|
if (SmallUpdates->isEmpty()) return;
|
||||||
|
|
||||||
|
auto smallList = createAndSwap(*SmallUpdates);
|
||||||
|
auto allList = createAndSwap(*AllUpdates);
|
||||||
|
for_const (auto &update, smallList) {
|
||||||
|
creator.notify(update.flags, update);
|
||||||
|
}
|
||||||
|
for_const (auto &update, allList) {
|
||||||
|
creator.notify(update.flags, update);
|
||||||
|
}
|
||||||
|
if (SmallUpdates->isEmpty()) {
|
||||||
|
std::swap(smallList, *SmallUpdates);
|
||||||
|
SmallUpdates->resize(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Notify
|
100
Telegram/SourceFiles/observer_peer.h
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
|
||||||
|
// Generic notifications about updates of some PeerData.
|
||||||
|
// You can subscribe to them by Notify::registerPeerObserver().
|
||||||
|
// 0x0000FFFFU for general peer updates (valid for any peer).
|
||||||
|
// 0xFFFF0000U for specific peer updates (valid for user / chat / channel).
|
||||||
|
struct PeerUpdate {
|
||||||
|
PeerUpdate(PeerData *updated = nullptr) : peer(updated) {
|
||||||
|
}
|
||||||
|
PeerData *peer;
|
||||||
|
|
||||||
|
enum class Flag {
|
||||||
|
NameChanged = 0x00000001U,
|
||||||
|
UsernameChanged = 0x00000002U,
|
||||||
|
PhotoChanged = 0x00000004U,
|
||||||
|
AboutChanged = 0x00000008U,
|
||||||
|
NotificationsEnabled = 0x00000010U,
|
||||||
|
SharedMediaChanged = 0x00000020U,
|
||||||
|
MigrationChanged = 0x00000040U,
|
||||||
|
|
||||||
|
// For chats and channels
|
||||||
|
InviteLinkChanged = 0x00000020U,
|
||||||
|
MembersChanged = 0x00000040U,
|
||||||
|
AdminsChanged = 0x00000080U,
|
||||||
|
|
||||||
|
UserCanShareContact = 0x00010000U,
|
||||||
|
UserIsContact = 0x00020000U,
|
||||||
|
UserPhoneChanged = 0x00040000U,
|
||||||
|
UserIsBlocked = 0x00080000U,
|
||||||
|
BotCommandsChanged = 0x00100000U,
|
||||||
|
UserOnlineChanged = 0x00200000U,
|
||||||
|
|
||||||
|
ChatCanEdit = 0x00010000U,
|
||||||
|
|
||||||
|
ChannelAmIn = 0x00010000U,
|
||||||
|
ChannelAmEditor = 0x00020000U,
|
||||||
|
ChannelCanEditPhoto = 0x00040000U,
|
||||||
|
ChannelCanAddMembers = 0x00080000U,
|
||||||
|
ChannelCanViewAdmins = 0x00100000U,
|
||||||
|
ChannelCanViewMembers = 0x00200000U,
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(Flags, Flag);
|
||||||
|
Flags flags = 0;
|
||||||
|
|
||||||
|
// NameChanged data
|
||||||
|
PeerData::Names oldNames;
|
||||||
|
PeerData::NameFirstChars oldNameFirstChars;
|
||||||
|
|
||||||
|
// SharedMediaChanged data
|
||||||
|
int32 mediaTypesMask = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdate::Flags);
|
||||||
|
|
||||||
|
void peerUpdatedDelayed(const PeerUpdate &update);
|
||||||
|
inline void peerUpdatedDelayed(PeerData *peer, PeerUpdate::Flags events) {
|
||||||
|
PeerUpdate update(peer);
|
||||||
|
update.flags = events;
|
||||||
|
peerUpdatedDelayed(update);
|
||||||
|
}
|
||||||
|
void peerUpdatedSendDelayed();
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
using PeerUpdateHandler = Function<void, const PeerUpdate&>;
|
||||||
|
ConnectionId plainRegisterPeerObserver(PeerUpdate::Flags events, PeerUpdateHandler &&handler);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename ObserverType>
|
||||||
|
void registerPeerObserver(PeerUpdate::Flags events, ObserverType *observer, void (ObserverType::*handler)(const PeerUpdate &)) {
|
||||||
|
auto connection = internal::plainRegisterPeerObserver(events, func(observer, handler));
|
||||||
|
observerRegistered(observer, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Notify
|
|
@ -611,6 +611,9 @@ void OverviewInner::onDragExec() {
|
||||||
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
|
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
|
||||||
}
|
}
|
||||||
drag->setMimeData(mimeData);
|
drag->setMimeData(mimeData);
|
||||||
|
|
||||||
|
// We don't receive mouseReleaseEvent when drag is finished.
|
||||||
|
ClickHandler::unpressed();
|
||||||
drag->exec(Qt::CopyAction);
|
drag->exec(Qt::CopyAction);
|
||||||
if (App::main()) App::main()->updateAfterDrag();
|
if (App::main()) App::main()->updateAfterDrag();
|
||||||
return;
|
return;
|
||||||
|
@ -639,6 +642,9 @@ void OverviewInner::onDragExec() {
|
||||||
}
|
}
|
||||||
|
|
||||||
drag->setMimeData(mimeData);
|
drag->setMimeData(mimeData);
|
||||||
|
|
||||||
|
// We don't receive mouseReleaseEvent when drag is finished.
|
||||||
|
ClickHandler::unpressed();
|
||||||
drag->exec(Qt::CopyAction);
|
drag->exec(Qt::CopyAction);
|
||||||
if (App::main()) App::main()->updateAfterDrag();
|
if (App::main()) App::main()->updateAfterDrag();
|
||||||
return;
|
return;
|
||||||
|
@ -1909,7 +1915,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp
|
||||||
, _scrollSetAfterShow(0)
|
, _scrollSetAfterShow(0)
|
||||||
, _scrollDelta(0)
|
, _scrollDelta(0)
|
||||||
, _selCount(0)
|
, _selCount(0)
|
||||||
, _sideShadow(this, st::shadowColor)
|
|
||||||
, _topShadow(this, st::shadowColor)
|
, _topShadow(this, st::shadowColor)
|
||||||
, _inGrab(false) {
|
, _inGrab(false) {
|
||||||
_scroll.setFocusPolicy(Qt::NoFocus);
|
_scroll.setFocusPolicy(Qt::NoFocus);
|
||||||
|
@ -1917,8 +1922,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp
|
||||||
_scroll.move(0, 0);
|
_scroll.move(0, 0);
|
||||||
_inner.move(0, 0);
|
_inner.move(0, 0);
|
||||||
|
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
|
|
||||||
updateScrollColors();
|
updateScrollColors();
|
||||||
|
|
||||||
_scroll.show();
|
_scroll.show();
|
||||||
|
@ -1970,8 +1973,6 @@ void OverviewWidget::resizeEvent(QResizeEvent *e) {
|
||||||
|
|
||||||
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
|
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
|
||||||
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
|
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
|
||||||
_sideShadow.resize(st::lineWidth, height());
|
|
||||||
_sideShadow.moveToLeft(0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverviewWidget::paintEvent(QPaintEvent *e) {
|
void OverviewWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
@ -1979,14 +1980,16 @@ void OverviewWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
if (_a_show.animating()) {
|
if (_a_show.animating()) {
|
||||||
|
int retina = cIntRetinaFactor();
|
||||||
|
int inCacheTop = st::topBarHeight;
|
||||||
if (a_coordOver.current() > 0) {
|
if (a_coordOver.current() > 0) {
|
||||||
p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor()));
|
p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * retina, inCacheTop * retina, a_coordOver.current() * retina, height() * retina));
|
||||||
p.setOpacity(a_shadow.current() * st::slideFadeOut);
|
p.setOpacity(a_progress.current() * st::slideFadeOut);
|
||||||
p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b);
|
p.fillRect(0, 0, a_coordOver.current(), height(), st::black);
|
||||||
p.setOpacity(1);
|
p.setOpacity(1);
|
||||||
}
|
}
|
||||||
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
|
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina));
|
||||||
p.setOpacity(a_shadow.current());
|
p.setOpacity(a_progress.current());
|
||||||
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
|
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2012,9 +2015,15 @@ void OverviewWidget::scrollReset() {
|
||||||
|
|
||||||
void OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) {
|
void OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) {
|
||||||
if (_a_show.animating()) {
|
if (_a_show.animating()) {
|
||||||
p.drawPixmap(a_coordUnder.current(), 0, _cacheTopBarUnder);
|
int retina = cIntRetinaFactor();
|
||||||
p.drawPixmap(a_coordOver.current(), 0, _cacheTopBarOver);
|
if (a_coordOver.current() > 0) {
|
||||||
p.setOpacity(a_shadow.current());
|
p.drawPixmap(QRect(0, 0, a_coordOver.current(), st::topBarHeight), _cacheUnder, QRect(-a_coordUnder.current() * retina, 0, a_coordOver.current() * retina, st::topBarHeight * retina));
|
||||||
|
p.setOpacity(a_progress.current() * st::slideFadeOut);
|
||||||
|
p.fillRect(0, 0, a_coordOver.current(), st::topBarHeight, st::black);
|
||||||
|
p.setOpacity(1);
|
||||||
|
}
|
||||||
|
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, st::topBarHeight), _cacheOver, QRect(0, 0, _cacheOver.width(), st::topBarHeight * retina));
|
||||||
|
p.setOpacity(a_progress.current());
|
||||||
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), st::topBarHeight), App::sprite(), st::slideShadow.rect());
|
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), st::topBarHeight), App::sprite(), st::slideShadow.rect());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2119,44 +2128,54 @@ void OverviewWidget::fastShow(bool back, int32 lastScrollTop) {
|
||||||
if (App::app()) App::app()->mtpUnpause();
|
if (App::app()) App::app()->mtpUnpause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverviewWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) {
|
void OverviewWidget::setLastScrollTop(int lastScrollTop) {
|
||||||
if (App::app()) App::app()->mtpPause();
|
|
||||||
|
|
||||||
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
|
|
||||||
(back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache;
|
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
_scroll.scrollToY(lastScrollTop < 0 ? countBestScroll() : lastScrollTop);
|
_scroll.scrollToY(lastScrollTop < 0 ? countBestScroll() : lastScrollTop);
|
||||||
(back ? _cacheUnder : _cacheOver) = myGrab(this);
|
}
|
||||||
App::main()->topBar()->stopAnim();
|
|
||||||
(back ? _cacheTopBarUnder : _cacheTopBarOver) = myGrab(App::main()->topBar());
|
void OverviewWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms) {
|
||||||
|
if (App::app()) App::app()->mtpPause();
|
||||||
|
|
||||||
|
resizeEvent(0);
|
||||||
|
|
||||||
|
_cacheUnder = params.oldContentCache;
|
||||||
|
show();
|
||||||
|
_topShadow.setVisible(params.withTopBarShadow ? false : true);
|
||||||
|
_cacheOver = App::main()->grabForShowAnimation(params);
|
||||||
|
_topShadow.setVisible(params.withTopBarShadow ? true : false);
|
||||||
App::main()->topBar()->startAnim();
|
App::main()->topBar()->startAnim();
|
||||||
|
|
||||||
_scrollSetAfterShow = _scroll.scrollTop();
|
_scrollSetAfterShow = _scroll.scrollTop();
|
||||||
_scroll.hide();
|
_scroll.hide();
|
||||||
_topShadow.hide();
|
|
||||||
|
|
||||||
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
|
int delta = st::slideShift;
|
||||||
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
if (direction == Window::SlideDirection::FromLeft) {
|
||||||
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
a_progress = anim::fvalue(1, 0);
|
||||||
|
std::swap(_cacheUnder, _cacheOver);
|
||||||
|
a_coordUnder = anim::ivalue(-delta, 0);
|
||||||
|
a_coordOver = anim::ivalue(0, width());
|
||||||
|
} else {
|
||||||
|
a_progress = anim::fvalue(0, 1);
|
||||||
|
a_coordUnder = anim::ivalue(0, -delta);
|
||||||
|
a_coordOver = anim::ivalue(width(), 0);
|
||||||
|
}
|
||||||
_a_show.start();
|
_a_show.start();
|
||||||
|
|
||||||
show();
|
|
||||||
|
|
||||||
App::main()->topBar()->update();
|
App::main()->topBar()->update();
|
||||||
_inner.activate();
|
|
||||||
|
activate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverviewWidget::step_show(float64 ms, bool timer) {
|
void OverviewWidget::step_show(float64 ms, bool timer) {
|
||||||
float64 dt = ms / st::slideDuration;
|
float64 dt = ms / st::slideDuration;
|
||||||
if (dt >= 1) {
|
if (dt >= 1) {
|
||||||
_a_show.stop();
|
_a_show.stop();
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
_topShadow.show();
|
_topShadow.show();
|
||||||
|
|
||||||
a_coordUnder.finish();
|
a_coordUnder.finish();
|
||||||
a_coordOver.finish();
|
a_coordOver.finish();
|
||||||
a_shadow.finish();
|
a_progress.finish();
|
||||||
_cacheUnder = _cacheOver = _cacheTopBarUnder = _cacheTopBarOver = QPixmap();
|
_cacheUnder = _cacheOver = QPixmap();
|
||||||
App::main()->topBar()->stopAnim();
|
App::main()->topBar()->stopAnim();
|
||||||
|
|
||||||
doneShow();
|
doneShow();
|
||||||
|
@ -2165,7 +2184,7 @@ void OverviewWidget::step_show(float64 ms, bool timer) {
|
||||||
} else {
|
} else {
|
||||||
a_coordUnder.update(dt, st::slideFunction);
|
a_coordUnder.update(dt, st::slideFunction);
|
||||||
a_coordOver.update(dt, st::slideFunction);
|
a_coordOver.update(dt, st::slideFunction);
|
||||||
a_shadow.update(dt, st::slideFunction);
|
a_progress.update(dt, st::slideFunction);
|
||||||
}
|
}
|
||||||
if (timer) {
|
if (timer) {
|
||||||
update();
|
update();
|
||||||
|
@ -2173,10 +2192,6 @@ void OverviewWidget::step_show(float64 ms, bool timer) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverviewWidget::updateAdaptiveLayout() {
|
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
}
|
|
||||||
|
|
||||||
void OverviewWidget::doneShow() {
|
void OverviewWidget::doneShow() {
|
||||||
_scroll.show();
|
_scroll.show();
|
||||||
_scroll.scrollToY(_scrollSetAfterShow);
|
_scroll.scrollToY(_scrollSetAfterShow);
|
||||||
|
@ -2308,6 +2323,8 @@ void OverviewWidget::onDeleteSelected() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverviewWidget::onDeleteSelectedSure() {
|
void OverviewWidget::onDeleteSelectedSure() {
|
||||||
|
Ui::hideLayer();
|
||||||
|
|
||||||
SelectedItemSet sel;
|
SelectedItemSet sel;
|
||||||
_inner.fillSelectedItems(sel);
|
_inner.fillSelectedItems(sel);
|
||||||
if (sel.isEmpty()) return;
|
if (sel.isEmpty()) return;
|
||||||
|
@ -2323,7 +2340,6 @@ void OverviewWidget::onDeleteSelectedSure() {
|
||||||
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
|
||||||
i.value()->destroy();
|
i.value()->destroy();
|
||||||
}
|
}
|
||||||
Ui::hideLayer();
|
|
||||||
|
|
||||||
for (QMap<PeerData*, QVector<MTPint> >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
|
for (QMap<PeerData*, QVector<MTPint> >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
|
||||||
App::main()->deleteMessages(i.key(), i.value());
|
App::main()->deleteMessages(i.key(), i.value());
|
||||||
|
@ -2331,6 +2347,8 @@ void OverviewWidget::onDeleteSelectedSure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverviewWidget::onDeleteContextSure() {
|
void OverviewWidget::onDeleteContextSure() {
|
||||||
|
Ui::hideLayer();
|
||||||
|
|
||||||
HistoryItem *item = App::contextItem();
|
HistoryItem *item = App::contextItem();
|
||||||
if (!item || item->type() != HistoryItemMsg) {
|
if (!item || item->type() != HistoryItemMsg) {
|
||||||
return;
|
return;
|
||||||
|
@ -2340,12 +2358,11 @@ void OverviewWidget::onDeleteContextSure() {
|
||||||
History *h = item->history();
|
History *h = item->history();
|
||||||
bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item);
|
bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item);
|
||||||
item->destroy();
|
item->destroy();
|
||||||
|
|
||||||
if (!wasOnServer && wasLast && !h->lastMsg) {
|
if (!wasOnServer && wasLast && !h->lastMsg) {
|
||||||
App::main()->checkPeerHistory(h->peer);
|
App::main()->checkPeerHistory(h->peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ui::hideLayer();
|
|
||||||
|
|
||||||
if (wasOnServer) {
|
if (wasOnServer) {
|
||||||
App::main()->deleteMessages(h->peer, toDelete);
|
App::main()->deleteMessages(h->peer, toDelete);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "window/section_widget.h"
|
||||||
|
|
||||||
namespace Overview {
|
namespace Overview {
|
||||||
namespace Layout {
|
namespace Layout {
|
||||||
|
|
||||||
|
@ -276,10 +278,13 @@ public:
|
||||||
int32 countBestScroll() const;
|
int32 countBestScroll() const;
|
||||||
|
|
||||||
void fastShow(bool back = false, int32 lastScrollTop = -1);
|
void fastShow(bool back = false, int32 lastScrollTop = -1);
|
||||||
void animShow(const QPixmap &oldAnimCache, const QPixmap &bgAnimTopBarCache, bool back = false, int32 lastScrollTop = -1);
|
bool hasTopBarShadow() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void setLastScrollTop(int lastScrollTop);
|
||||||
|
void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams ¶ms);
|
||||||
void step_show(float64 ms, bool timer);
|
void step_show(float64 ms, bool timer);
|
||||||
|
|
||||||
void updateAdaptiveLayout();
|
|
||||||
void doneShow();
|
void doneShow();
|
||||||
|
|
||||||
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
|
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
|
||||||
|
@ -300,14 +305,17 @@ public:
|
||||||
void updateAfterDrag();
|
void updateAfterDrag();
|
||||||
|
|
||||||
void grabStart() override {
|
void grabStart() override {
|
||||||
_sideShadow.hide();
|
|
||||||
_inGrab = true;
|
_inGrab = true;
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
}
|
}
|
||||||
|
void grapWithoutTopBarShadow() {
|
||||||
|
grabStart();
|
||||||
|
_topShadow.hide();
|
||||||
|
}
|
||||||
void grabFinish() override {
|
void grabFinish() override {
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
_inGrab = false;
|
_inGrab = false;
|
||||||
resizeEvent(0);
|
resizeEvent(0);
|
||||||
|
_topShadow.show();
|
||||||
}
|
}
|
||||||
void rpcClear() override {
|
void rpcClear() override {
|
||||||
_inner.rpcClear();
|
_inner.rpcClear();
|
||||||
|
@ -343,9 +351,9 @@ private:
|
||||||
QString _header;
|
QString _header;
|
||||||
|
|
||||||
Animation _a_show;
|
Animation _a_show;
|
||||||
QPixmap _cacheUnder, _cacheOver, _cacheTopBarUnder, _cacheTopBarOver;
|
QPixmap _cacheUnder, _cacheOver;
|
||||||
anim::ivalue a_coordUnder, a_coordOver;
|
anim::ivalue a_coordUnder, a_coordOver;
|
||||||
anim::fvalue a_shadow;
|
anim::fvalue a_progress;
|
||||||
|
|
||||||
int32 _scrollSetAfterShow;
|
int32 _scrollSetAfterShow;
|
||||||
|
|
||||||
|
@ -354,7 +362,7 @@ private:
|
||||||
|
|
||||||
int32 _selCount;
|
int32 _selCount;
|
||||||
|
|
||||||
PlainShadow _sideShadow, _topShadow;
|
PlainShadow _topShadow;
|
||||||
bool _inGrab;
|
bool _inGrab;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,7 +119,7 @@ void PasscodeWidget::animShow(const QPixmap &bgAnimCache, bool back) {
|
||||||
(back ? _cacheUnder : _cacheOver) = myGrab(this);
|
(back ? _cacheUnder : _cacheOver) = myGrab(this);
|
||||||
hideAll();
|
hideAll();
|
||||||
|
|
||||||
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
|
a_coordUnder = back ? anim::ivalue(-st::slideShift, 0) : anim::ivalue(0, -st::slideShift);
|
||||||
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
|
||||||
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
|
||||||
_a_show.start();
|
_a_show.start();
|
||||||
|
|
|
@ -33,12 +33,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
|
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
|
||||||
, _a_state(animation(this, &PlayerWidget::step_state))
|
, _a_state(animation(this, &PlayerWidget::step_state))
|
||||||
, _a_progress(animation(this, &PlayerWidget::step_progress))
|
, _a_progress(animation(this, &PlayerWidget::step_progress)) {
|
||||||
, _sideShadow(this, st::shadowColor) {
|
|
||||||
resize(st::wndMinWidth, st::playerHeight);
|
resize(st::wndMinWidth, st::playerHeight);
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
memset(_stateHovers, 0, sizeof(_stateHovers));
|
memset(_stateHovers, 0, sizeof(_stateHovers));
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerWidget::paintEvent(QPaintEvent *e) {
|
void PlayerWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
@ -343,10 +341,6 @@ void PlayerWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerWidget::updateAdaptiveLayout() {
|
|
||||||
_sideShadow.setVisible(!Adaptive::OneColumn());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlayerWidget::seekingSong(const SongMsgId &song) const {
|
bool PlayerWidget::seekingSong(const SongMsgId &song) const {
|
||||||
return (_down == OverPlayback) && (song == _song);
|
return (_down == OverPlayback) && (song == _song);
|
||||||
}
|
}
|
||||||
|
@ -570,9 +564,6 @@ void PlayerWidget::resizeEvent(QResizeEvent *e) {
|
||||||
int32 infoLeft = (_fullAvailable ? (_nextRect.x() + _nextRect.width()) : (_playRect.x() + _playRect.width()));
|
int32 infoLeft = (_fullAvailable ? (_nextRect.x() + _nextRect.width()) : (_playRect.x() + _playRect.width()));
|
||||||
_infoRect = QRect(infoLeft + st::playerSkip / 2, 0, (_fullAvailable ? _fullRect.x() : _repeatRect.x()) - infoLeft - st::playerSkip, availh);
|
_infoRect = QRect(infoLeft + st::playerSkip / 2, 0, (_fullAvailable ? _fullRect.x() : _repeatRect.x()) - infoLeft - st::playerSkip, availh);
|
||||||
|
|
||||||
_sideShadow.resize(st::lineWidth, height());
|
|
||||||
_sideShadow.moveToLeft(0, 0);
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,6 @@ public:
|
||||||
void clearSelection();
|
void clearSelection();
|
||||||
|
|
||||||
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
|
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
|
||||||
void updateAdaptiveLayout();
|
|
||||||
|
|
||||||
bool seekingSong(const SongMsgId &song) const;
|
bool seekingSong(const SongMsgId &song) const;
|
||||||
|
|
||||||
|
@ -136,6 +135,4 @@ private:
|
||||||
anim::fvalue a_loadProgress = { 0., 0. };
|
anim::fvalue a_loadProgress = { 0., 0. };
|
||||||
Animation _a_progress;
|
Animation _a_progress;
|
||||||
|
|
||||||
PlainShadow _sideShadow;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
169
Telegram/SourceFiles/profile/profile.style
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
using "basic.style";
|
||||||
|
using "basic_types.style";
|
||||||
|
|
||||||
|
profileBg: windowBg;
|
||||||
|
|
||||||
|
profileTopBarHeight: topBarHeight;
|
||||||
|
profileTopBarBackIconFg: #51b3e0;
|
||||||
|
profileTopBarBackIcon: icon {
|
||||||
|
{ "topbar_back_arrow", profileTopBarBackIconFg },
|
||||||
|
};
|
||||||
|
profileTopBarBackIconPosition: point(15px, 19px);
|
||||||
|
profileTopBarBackFont: font(14px);
|
||||||
|
profileTopBarBackFg: #1485c2;
|
||||||
|
profileTopBarBackPosition: point(32px, 17px);
|
||||||
|
profileFixedBarButton: flatButton(topBarButton) {
|
||||||
|
}
|
||||||
|
|
||||||
|
profileMarginTop: 13px;
|
||||||
|
profilePhotoSize: 112px;
|
||||||
|
profilePhotoLeftMin: 18px;
|
||||||
|
profilePhotoLeftMax: 45px;
|
||||||
|
profilePhotoDuration: 500;
|
||||||
|
profileNameLeft: 26px;
|
||||||
|
profileNameTop: 9px;
|
||||||
|
profileNameLabel: flatLabel(labelDefFlat) {
|
||||||
|
margin: margins(10px, 5px, 10px, 5px);
|
||||||
|
font: font(16px);
|
||||||
|
width: 160px;
|
||||||
|
maxHeight: 24px;
|
||||||
|
}
|
||||||
|
profileNameTextStyle: textStyle(defaultTextStyle) {
|
||||||
|
}
|
||||||
|
profileStatusLeft: 27px;
|
||||||
|
profileStatusTop: 35px;
|
||||||
|
profileStatusFont: normalFont;
|
||||||
|
profileStatusFg: windowSubTextFg;
|
||||||
|
profileStatusFgActive: windowActiveTextFg;
|
||||||
|
profileMarginBottom: 30px;
|
||||||
|
|
||||||
|
profileActiveBg: #3fb0e4;
|
||||||
|
profileButtonLeft: 27px;
|
||||||
|
profileButtonTop: 88px;
|
||||||
|
profileButtonSkip: 10px;
|
||||||
|
profilePrimaryButton: BoxButton {
|
||||||
|
textFg: #ffffff;
|
||||||
|
textFgOver: #ffffff;
|
||||||
|
textBg: profileActiveBg;
|
||||||
|
textBgOver: profileActiveBg;
|
||||||
|
|
||||||
|
width: -34px;
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
textTop: 8px;
|
||||||
|
|
||||||
|
font: semiboldFont;
|
||||||
|
duration: 200;
|
||||||
|
}
|
||||||
|
profileSecondaryButton: BoxButton(profilePrimaryButton) {
|
||||||
|
textFg: #189dda;
|
||||||
|
textFgOver: #189dda;
|
||||||
|
textBg: #ffffff;
|
||||||
|
textBgOver: #f2f7fa;
|
||||||
|
}
|
||||||
|
profileAddMemberIcon: icon {
|
||||||
|
{ "profile_add_member", profileActiveBg, point(20px, 10px) },
|
||||||
|
};
|
||||||
|
profileAddMemberButton: BoxButton(profileSecondaryButton) {
|
||||||
|
width: 62px;
|
||||||
|
icon: profileAddMemberIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
profileDropAreaBg: profileBg;
|
||||||
|
profileDropAreaFg: profileActiveBg;
|
||||||
|
profileDropAreaPadding: margins(25px, 3px, 25px, 20px);
|
||||||
|
profileDropAreaTitleFont: font(24px);
|
||||||
|
profileDropAreaTitleTop: 30px;
|
||||||
|
profileDropAreaSubtitleFont: font(16px);
|
||||||
|
profileDropAreaSubtitleTop: 68px;
|
||||||
|
profileDropAreaBorderFg: profileDropAreaFg;
|
||||||
|
profileDropAreaBorderWidth: 3px;
|
||||||
|
profileDropAreaDuration: 200;
|
||||||
|
|
||||||
|
profileDividerFg: windowShadowFg;
|
||||||
|
profileDividerLeft: icon {
|
||||||
|
{ "profile_divider_left", profileDividerFg },
|
||||||
|
};
|
||||||
|
profileDividerFill: icon {
|
||||||
|
{ "profile_divider_fill", profileDividerFg },
|
||||||
|
};
|
||||||
|
|
||||||
|
profileBlocksTop: 7px;
|
||||||
|
profileBlocksBottom: 20px;
|
||||||
|
profileBlockLeftMin: 8px;
|
||||||
|
profileBlockLeftMax: 25px;
|
||||||
|
profileBlockNarrowWidthMin: 220px;
|
||||||
|
profileBlockWideWidthMin: 300px;
|
||||||
|
profileBlockWideWidthMax: 340px;
|
||||||
|
profileBlockMarginTop: 14px;
|
||||||
|
profileBlockMarginRight: 10px;
|
||||||
|
profileBlockMarginBottom: 4px;
|
||||||
|
profileBlockTitleHeight: 25px;
|
||||||
|
profileBlockTitleFont: font(14px semibold);
|
||||||
|
profileBlockTitleFg: black;
|
||||||
|
profileBlockTitlePosition: point(24px, 0px);
|
||||||
|
profileBlockLabel: flatLabel(labelDefFlat) {
|
||||||
|
textFg: windowSubTextFg;
|
||||||
|
}
|
||||||
|
profileBlockTextPart: flatLabel(labelDefFlat) {
|
||||||
|
width: 180px;
|
||||||
|
margin: margins(5px, 5px, 5px, 5px);
|
||||||
|
}
|
||||||
|
profileBlockOneLineTextPart: flatLabel(profileBlockTextPart) {
|
||||||
|
width: 0px; // No need to set minWidth in one-line text.
|
||||||
|
maxHeight: 20px;
|
||||||
|
}
|
||||||
|
profileBlockOneLineSkip: 9px;
|
||||||
|
profileBlockOneLineWidthMax: 240px;
|
||||||
|
|
||||||
|
profileInviteLinkText: flatLabel(profileBlockTextPart) {
|
||||||
|
width: 1px; // Required for BreakEverywhere
|
||||||
|
}
|
||||||
|
|
||||||
|
profileLimitReachedSkip: 6px;
|
||||||
|
|
||||||
|
profileMemberHeight: 58px;
|
||||||
|
profileMemberPaddingLeft: 16px;
|
||||||
|
profileMemberPhotoSize: 46px;
|
||||||
|
profileMemberPhotoPosition: point(12px, 6px);
|
||||||
|
profileMemberNamePosition: point(68px, 11px);
|
||||||
|
profileMemberNameFg: windowTextFg;
|
||||||
|
profileMemberStatusPosition: point(68px, 31px);
|
||||||
|
profileMemberStatusFg: windowSubTextFg;
|
||||||
|
profileMemberStatusFgOver: windowSubTextFg;
|
||||||
|
profileMemberStatusFgActive: windowActiveTextFg;
|
||||||
|
profileMemberAdminIcon: icon {
|
||||||
|
{ "profile_admin_star", profileActiveBg, point(4px, 2px) },
|
||||||
|
};
|
||||||
|
profileLimitReachedLabel: flatLabel(labelDefFlat) {
|
||||||
|
width: 180px;
|
||||||
|
margin: margins(profileMemberPaddingLeft, 9px, profileMemberPaddingLeft, 6px);
|
||||||
|
}
|
||||||
|
profileLimitReachedStyle: textStyle(defaultTextStyle) {
|
||||||
|
lineHeight: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
profileReportReasonOther: InputArea(defaultInputArea) {
|
||||||
|
textMargins: margins(1px, 6px, 1px, 4px);
|
||||||
|
heightMax: 115px;
|
||||||
|
}
|
363
Telegram/SourceFiles/profile/profile_actions_widget.cpp
Normal file
|
@ -0,0 +1,363 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_actions_widget.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "ui/buttons/left_outline_button.h"
|
||||||
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "boxes/report_box.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "lang.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||||
|
|
||||||
|
ActionsWidget::ActionsWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_actions_section)) {
|
||||||
|
auto observeEvents = UpdateFlag::ChannelAmIn
|
||||||
|
| UpdateFlag::UserIsBlocked
|
||||||
|
| UpdateFlag::BotCommandsChanged
|
||||||
|
| UpdateFlag::MembersChanged;
|
||||||
|
Notify::registerPeerObserver(observeEvents, this, &ActionsWidget::notifyPeerUpdated);
|
||||||
|
|
||||||
|
validateBlockStatus();
|
||||||
|
refreshButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer != peer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto needFullRefresh = [&update, this]() {
|
||||||
|
if (update.flags & UpdateFlag::BotCommandsChanged) {
|
||||||
|
if (_hasBotHelp != hasBotCommand(qsl("help")) || _hasBotSettings != hasBotCommand(qsl("settings"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (needFullRefresh()) {
|
||||||
|
refreshButtons();
|
||||||
|
} else {
|
||||||
|
if (update.flags & UpdateFlag::MembersChanged) {
|
||||||
|
refreshDeleteChannel();
|
||||||
|
}
|
||||||
|
if (update.flags & UpdateFlag::ChannelAmIn) {
|
||||||
|
refreshLeaveChannel();
|
||||||
|
}
|
||||||
|
if (update.flags & UpdateFlag::UserIsBlocked) {
|
||||||
|
refreshBlockUser();
|
||||||
|
}
|
||||||
|
refreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
contentSizeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::validateBlockStatus() const {
|
||||||
|
auto needFullPeer = [this]() {
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
if (user->blockStatus() == UserData::BlockStatus::Unknown) {
|
||||||
|
return true;
|
||||||
|
} else if (user->botInfo && !user->botInfo->inited) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if (needFullPeer()) {
|
||||||
|
if (App::api()) App::api()->requestFullPeer(peer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui::LeftOutlineButton *ActionsWidget::addButton(const QString &text, const char *slot, const style::OutlineButton &st, int skipHeight) {
|
||||||
|
auto result = new Ui::LeftOutlineButton(this, text, st);
|
||||||
|
connect(result, SIGNAL(clicked()), this, slot);
|
||||||
|
result->show();
|
||||||
|
|
||||||
|
int top = buttonsBottom() + skipHeight;
|
||||||
|
resizeButton(result, top);
|
||||||
|
|
||||||
|
_buttons.push_back(result);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ActionsWidget::resizeButton(Ui::LeftOutlineButton *button, int top) {
|
||||||
|
int left = defaultOutlineButtonLeft();
|
||||||
|
int availableWidth = width() - left - st::profileBlockMarginRight;
|
||||||
|
accumulate_min(availableWidth, st::profileBlockOneLineWidthMax);
|
||||||
|
button->resizeToWidth(availableWidth);
|
||||||
|
button->moveToLeft(left, top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::refreshButtons() {
|
||||||
|
auto buttons = createAndSwap(_buttons);
|
||||||
|
for_const (auto &button, buttons) {
|
||||||
|
delete button;
|
||||||
|
}
|
||||||
|
_blockUser = _leaveChannel = nullptr;
|
||||||
|
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
if ((_hasBotHelp = hasBotCommand(qsl("help")))) {
|
||||||
|
addButton(lang(lng_profile_bot_help), SLOT(onBotHelp()));
|
||||||
|
}
|
||||||
|
if ((_hasBotSettings = hasBotCommand(qsl("settings")))) {
|
||||||
|
addButton(lang(lng_profile_bot_settings), SLOT(onBotSettings()));
|
||||||
|
}
|
||||||
|
addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
|
||||||
|
addButton(lang(lng_profile_delete_conversation), SLOT(onDeleteConversation()));
|
||||||
|
refreshBlockUser();
|
||||||
|
} else if (auto chat = peer()->asChat()) {
|
||||||
|
if (chat->amCreator()) {
|
||||||
|
addButton(lang(lng_profile_migrate_button), SLOT(onUpgradeToSupergroup()));
|
||||||
|
}
|
||||||
|
addButton(lang(lng_profile_clear_history), SLOT(onClearHistory()));
|
||||||
|
addButton(lang(lng_profile_clear_and_exit), SLOT(onDeleteConversation()));
|
||||||
|
} else if (auto channel = peer()->asChannel()) {
|
||||||
|
if (!channel->amCreator()) {
|
||||||
|
addButton(lang(lng_profile_report), SLOT(onReport()));
|
||||||
|
}
|
||||||
|
refreshDeleteChannel();
|
||||||
|
refreshLeaveChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::refreshVisibility() {
|
||||||
|
setVisible(!_buttons.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ActionsWidget::getBlockButtonText() const {
|
||||||
|
auto user = peer()->asUser();
|
||||||
|
if (!user || (user->id == peerFromUser(MTP::authedId()))) return QString();
|
||||||
|
if (user->blockStatus() == UserData::BlockStatus::Unknown) return QString();
|
||||||
|
|
||||||
|
if (user->isBlocked()) {
|
||||||
|
if (user->botInfo) {
|
||||||
|
return lang(lng_profile_unblock_bot);
|
||||||
|
}
|
||||||
|
return lang(lng_profile_unblock_user);
|
||||||
|
} else if (user->botInfo) {
|
||||||
|
return lang(lng_profile_block_bot);
|
||||||
|
}
|
||||||
|
return lang(lng_profile_block_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ActionsWidget::hasBotCommand(const QString &command) const {
|
||||||
|
auto user = peer()->asUser();
|
||||||
|
if (!user || !user->botInfo || user->botInfo->commands.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_const (auto &cmd, user->botInfo->commands) {
|
||||||
|
if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::sendBotCommand(const QString &command) {
|
||||||
|
auto user = peer()->asUser();
|
||||||
|
if (user && user->botInfo && !user->botInfo->commands.isEmpty()) {
|
||||||
|
for_const (auto &cmd, user->botInfo->commands) {
|
||||||
|
if (!cmd.command.compare(command, Qt::CaseInsensitive)) {
|
||||||
|
Ui::showPeerHistory(user, ShowAtTheEndMsgId);
|
||||||
|
App::sendBotCommand(user, user, '/' + cmd.command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command was not found.
|
||||||
|
refreshButtons();
|
||||||
|
contentSizeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::refreshBlockUser() {
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
auto blockText = getBlockButtonText();
|
||||||
|
if (_blockUser) {
|
||||||
|
if (blockText.isEmpty()) {
|
||||||
|
_buttons.removeOne(_blockUser);
|
||||||
|
delete _blockUser;
|
||||||
|
_blockUser = nullptr;
|
||||||
|
} else {
|
||||||
|
_blockUser->setText(blockText);
|
||||||
|
}
|
||||||
|
} else if (!blockText.isEmpty()) {
|
||||||
|
_blockUser = addButton(blockText, SLOT(onBlockUser()), st::attentionLeftOutlineButton, st::profileBlockOneLineSkip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::refreshDeleteChannel() {
|
||||||
|
if (auto channel = peer()->asChannel()) {
|
||||||
|
if (channel->canDelete() && !_deleteChannel) {
|
||||||
|
_deleteChannel = addButton(lang(channel->isMegagroup() ? lng_profile_delete_group : lng_profile_delete_channel), SLOT(onDeleteChannel()), st::attentionLeftOutlineButton);
|
||||||
|
} else if (!channel->canDelete() && _deleteChannel) {
|
||||||
|
_buttons.removeOne(_deleteChannel);
|
||||||
|
delete _deleteChannel;
|
||||||
|
_deleteChannel = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::refreshLeaveChannel() {
|
||||||
|
if (auto channel = peer()->asChannel()) {
|
||||||
|
if (!channel->amCreator()) {
|
||||||
|
if (channel->amIn() && !_leaveChannel) {
|
||||||
|
_leaveChannel = addButton(lang(channel->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel), SLOT(onLeaveChannel()));
|
||||||
|
} else if (!channel->amIn() && _leaveChannel) {
|
||||||
|
_buttons.removeOne(_leaveChannel);
|
||||||
|
delete _leaveChannel;
|
||||||
|
_leaveChannel = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ActionsWidget::resizeGetHeight(int newWidth) {
|
||||||
|
for_const (auto button, _buttons) {
|
||||||
|
resizeButton(button, button->y());
|
||||||
|
}
|
||||||
|
return buttonsBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ActionsWidget::buttonsBottom() const {
|
||||||
|
if (_buttons.isEmpty()) {
|
||||||
|
return contentTop();
|
||||||
|
}
|
||||||
|
auto lastButton = _buttons.back();
|
||||||
|
return lastButton->y() + lastButton->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onBotHelp() {
|
||||||
|
sendBotCommand(qsl("help"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onBotSettings() {
|
||||||
|
sendBotCommand(qsl("settings"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onClearHistory() {
|
||||||
|
QString confirmation;
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
|
||||||
|
} else if (auto chat = peer()->asChat()) {
|
||||||
|
confirmation = lng_sure_delete_group_history(lt_group, App::peerName(peer()));
|
||||||
|
}
|
||||||
|
if (!confirmation.isEmpty()) {
|
||||||
|
auto box = new ConfirmBox(confirmation, lang(lng_box_delete), st::attentionBoxButton);
|
||||||
|
connect(box, SIGNAL(confirmed()), this, SLOT(onClearHistorySure()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onClearHistorySure() {
|
||||||
|
Ui::hideLayer();
|
||||||
|
App::main()->clearHistory(peer());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onDeleteConversation() {
|
||||||
|
QString confirmation, confirmButton;
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
confirmation = lng_sure_delete_history(lt_contact, App::peerName(peer()));
|
||||||
|
confirmButton = lang(lng_box_delete);
|
||||||
|
} else if (auto chat = peer()->asChat()) {
|
||||||
|
confirmation = lng_sure_delete_and_exit(lt_group, App::peerName(peer()));
|
||||||
|
confirmButton = lang(lng_box_leave);
|
||||||
|
}
|
||||||
|
if (!confirmation.isEmpty()) {
|
||||||
|
auto box = new ConfirmBox(confirmation, confirmButton, st::attentionBoxButton);
|
||||||
|
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteConversationSure()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onDeleteConversationSure() {
|
||||||
|
Ui::hideLayer();
|
||||||
|
Ui::showChatsList();
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
App::main()->deleteConversation(peer());
|
||||||
|
} else if (auto chat = peer()->asChat()) {
|
||||||
|
App::main()->deleteAndExit(chat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onBlockUser() {
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
if (user->isBlocked()) {
|
||||||
|
App::api()->unblockUser(user);
|
||||||
|
} else {
|
||||||
|
App::api()->blockUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onUpgradeToSupergroup() {
|
||||||
|
if (auto chat = peer()->asChat()) {
|
||||||
|
Ui::showLayer(new ConvertToSupergroupBox(chat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onDeleteChannel() {
|
||||||
|
auto box = new ConfirmBox(lang(peer()->isMegagroup() ? lng_sure_delete_group : lng_sure_delete_channel), lang(lng_box_delete), st::attentionBoxButton);
|
||||||
|
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteChannelSure()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onDeleteChannelSure() {
|
||||||
|
Ui::hideLayer();
|
||||||
|
Ui::showChatsList();
|
||||||
|
if (auto chat = peer()->migrateFrom()) {
|
||||||
|
App::main()->deleteAndExit(chat);
|
||||||
|
}
|
||||||
|
if (auto channel = peer()->asChannel()) {
|
||||||
|
MTP::send(MTPchannels_DeleteChannel(channel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::deleteChannelFailed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onLeaveChannel() {
|
||||||
|
auto channel = peer()->asChannel();
|
||||||
|
if (!channel) return;
|
||||||
|
|
||||||
|
auto box = new ConfirmBox(lang(channel->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel), lang(lng_box_leave));
|
||||||
|
connect(box, SIGNAL(confirmed()), this, SLOT(onLeaveChannelSure()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onLeaveChannelSure() {
|
||||||
|
App::api()->leaveChannel(peer()->asChannel());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActionsWidget::onReport() {
|
||||||
|
if (auto channel = peer()->asChannel()) {
|
||||||
|
Ui::showLayer(new ReportBox(channel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
102
Telegram/SourceFiles/profile/profile_actions_widget.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "profile/profile_block_widget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class LeftOutlineButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
struct PeerUpdate;
|
||||||
|
} // namespace Notify
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class ActionsWidget : public BlockWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
ActionsWidget(QWidget *parent, PeerData *peer);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Resizes content and counts natural widget height for the desired width.
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onBotHelp();
|
||||||
|
void onBotSettings();
|
||||||
|
void onClearHistory();
|
||||||
|
void onClearHistorySure();
|
||||||
|
void onDeleteConversation();
|
||||||
|
void onDeleteConversationSure();
|
||||||
|
void onBlockUser();
|
||||||
|
void onUpgradeToSupergroup();
|
||||||
|
void onDeleteChannel();
|
||||||
|
void onDeleteChannelSure();
|
||||||
|
void onLeaveChannel();
|
||||||
|
void onLeaveChannelSure();
|
||||||
|
void onReport();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Observed notifications.
|
||||||
|
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||||
|
|
||||||
|
void validateBlockStatus() const;
|
||||||
|
|
||||||
|
int buttonsBottom() const;
|
||||||
|
|
||||||
|
void refreshButtons();
|
||||||
|
void refreshBlockUser();
|
||||||
|
void refreshDeleteChannel();
|
||||||
|
void refreshLeaveChannel();
|
||||||
|
void refreshVisibility();
|
||||||
|
|
||||||
|
Ui::LeftOutlineButton *addButton(const QString &text, const char *slot
|
||||||
|
, const style::OutlineButton &st = st::defaultLeftOutlineButton, int skipHeight = 0);
|
||||||
|
void resizeButton(Ui::LeftOutlineButton *button, int top);
|
||||||
|
|
||||||
|
QString getBlockButtonText() const;
|
||||||
|
bool hasBotCommand(const QString &command) const;
|
||||||
|
void sendBotCommand(const QString &command);
|
||||||
|
|
||||||
|
QList<Ui::LeftOutlineButton*> _buttons;
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _botHelp = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _botSettings = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _reportChannel = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _leaveChannel = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _deleteChannel = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _upgradeToSupergroup = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _clearHistory = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _deleteConversation = { nullptr };
|
||||||
|
//ChildWidget<Ui::LeftOutlineButton> _blockUser = { nullptr };
|
||||||
|
|
||||||
|
// Hold some button pointers to update / toggle them.
|
||||||
|
bool _hasBotHelp = false;
|
||||||
|
bool _hasBotSettings = false;
|
||||||
|
Ui::LeftOutlineButton *_blockUser = nullptr;
|
||||||
|
Ui::LeftOutlineButton *_deleteChannel = nullptr;
|
||||||
|
Ui::LeftOutlineButton *_leaveChannel = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Profile
|
57
Telegram/SourceFiles/profile/profile_block_widget.cpp
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_block_widget.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
BlockWidget::BlockWidget(QWidget *parent, PeerData *peer, const QString &title) : TWidget(parent)
|
||||||
|
, _peer(peer)
|
||||||
|
, _title(title) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockWidget::resizeToWidth(int newWidth) {
|
||||||
|
resize(newWidth, resizeGetHeight(newWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockWidget::contentTop() const {
|
||||||
|
return st::profileBlockMarginTop + st::profileBlockTitleHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
p.setFont(st::profileBlockTitleFont);
|
||||||
|
p.setPen(st::profileBlockTitleFg);
|
||||||
|
int titleLeft = st::profileBlockTitlePosition.x();
|
||||||
|
int titleTop = st::profileBlockMarginTop + st::profileBlockTitlePosition.y();
|
||||||
|
p.drawTextLeft(titleLeft, titleTop, width(), _title);
|
||||||
|
|
||||||
|
paintContents(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int defaultOutlineButtonLeft() {
|
||||||
|
return st::profileBlockTitlePosition.x() - st::defaultLeftOutlineButton.padding.left();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
74
Telegram/SourceFiles/profile/profile_block_widget.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class BlockWidget : public TWidget, public Notify::Observer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
BlockWidget(QWidget *parent, PeerData *peer, const QString &title);
|
||||||
|
|
||||||
|
// Count new height for width=newWidth and resize to it.
|
||||||
|
void resizeToWidth(int newWidth);
|
||||||
|
|
||||||
|
// Updates the area that is visible inside the scroll container.
|
||||||
|
virtual void setVisibleTopBottom(int visibleTop, int visibleBottom) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~BlockWidget() {
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void heightUpdated();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
virtual void paintContents(Painter &p) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Where does the block content start (after the title).
|
||||||
|
int contentTop() const;
|
||||||
|
|
||||||
|
// Resizes content and counts natural widget height for the desired width.
|
||||||
|
virtual int resizeGetHeight(int newWidth) = 0;
|
||||||
|
|
||||||
|
void contentSizeUpdated() {
|
||||||
|
resizeToWidth(width());
|
||||||
|
emit heightUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
PeerData *peer() const {
|
||||||
|
return _peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PeerData *_peer;
|
||||||
|
QString _title;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
int defaultOutlineButtonLeft();
|
||||||
|
|
||||||
|
} // namespace Profile
|
499
Telegram/SourceFiles/profile/profile_cover.cpp
Normal file
|
@ -0,0 +1,499 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_cover.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "profile/profile_cover_drop_area.h"
|
||||||
|
#include "profile/profile_userpic_button.h"
|
||||||
|
#include "ui/buttons/round_button.h"
|
||||||
|
#include "ui/filedialog.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "boxes/contactsbox.h"
|
||||||
|
#include "boxes/photocropbox.h"
|
||||||
|
#include "lang.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||||
|
const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact
|
||||||
|
| UpdateFlag::ChatCanEdit
|
||||||
|
| UpdateFlag::ChannelCanEditPhoto
|
||||||
|
| UpdateFlag::ChannelCanAddMembers
|
||||||
|
| UpdateFlag::ChannelAmIn;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||||
|
, _peer(peer)
|
||||||
|
, _peerUser(peer->asUser())
|
||||||
|
, _peerChat(peer->asChat())
|
||||||
|
, _peerChannel(peer->asChannel())
|
||||||
|
, _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr)
|
||||||
|
, _userpicButton(this, peer)
|
||||||
|
, _name(this, st::profileNameLabel) {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
setAcceptDrops(true);
|
||||||
|
|
||||||
|
_name.setSelectable(true);
|
||||||
|
_name.setContextCopyText(lang(lng_profile_copy_fullname));
|
||||||
|
|
||||||
|
auto observeEvents = ButtonsUpdateFlags
|
||||||
|
| UpdateFlag::NameChanged
|
||||||
|
| UpdateFlag::UserOnlineChanged;
|
||||||
|
Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
|
||||||
|
FileDialog::registerObserver(this, &CoverWidget::notifyFileQueryUpdated);
|
||||||
|
|
||||||
|
connect(_userpicButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
|
||||||
|
validatePhoto();
|
||||||
|
|
||||||
|
refreshNameText();
|
||||||
|
refreshStatusText();
|
||||||
|
|
||||||
|
refreshButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotoData *CoverWidget::validatePhoto() const {
|
||||||
|
PhotoData *photo = (_peer->photoId && _peer->photoId != UnknownPeerPhotoId) ? App::photo(_peer->photoId) : nullptr;
|
||||||
|
if ((_peer->photoId == UnknownPeerPhotoId) || (_peer->photoId && (!photo || !photo->date))) {
|
||||||
|
App::api()->requestFullPeer(_peer);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return photo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onPhotoShow() {
|
||||||
|
if (auto photo = validatePhoto()) {
|
||||||
|
App::wnd()->showPhoto(photo, _peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CoverWidget::countPhotoLeft(int newWidth) const {
|
||||||
|
int result = st::profilePhotoLeftMin;
|
||||||
|
result += (newWidth - st::wndMinWidth) / 2;
|
||||||
|
return qMin(result, st::profilePhotoLeftMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::resizeToWidth(int newWidth) {
|
||||||
|
int newHeight = 0;
|
||||||
|
|
||||||
|
newHeight += st::profileMarginTop;
|
||||||
|
|
||||||
|
_photoLeft = countPhotoLeft(newWidth);
|
||||||
|
_userpicButton->moveToLeft(_photoLeft, newHeight);
|
||||||
|
|
||||||
|
refreshNameGeometry(newWidth);
|
||||||
|
|
||||||
|
int infoLeft = _userpicButton->x() + _userpicButton->width();
|
||||||
|
_statusPosition = QPoint(infoLeft + st::profileStatusLeft, _userpicButton->y() + st::profileStatusTop);
|
||||||
|
|
||||||
|
moveAndToggleButtons(newWidth);
|
||||||
|
|
||||||
|
newHeight += st::profilePhotoSize;
|
||||||
|
newHeight += st::profileMarginBottom;
|
||||||
|
|
||||||
|
_dividerTop = newHeight;
|
||||||
|
newHeight += st::profileDividerFill.height();
|
||||||
|
|
||||||
|
newHeight += st::profileBlocksTop;
|
||||||
|
|
||||||
|
resizeDropArea();
|
||||||
|
resize(newWidth, newHeight);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::refreshNameGeometry(int newWidth) {
|
||||||
|
int infoLeft = _userpicButton->x() + _userpicButton->width();
|
||||||
|
int nameLeft = infoLeft + st::profileNameLeft - st::profileNameLabel.margin.left();
|
||||||
|
int nameTop = _userpicButton->y() + st::profileNameTop - st::profileNameLabel.margin.top();
|
||||||
|
int nameWidth = newWidth - infoLeft - st::profileNameLeft - st::profileButtonSkip;
|
||||||
|
int marginsAdd = st::profileNameLabel.margin.left() + st::profileNameLabel.margin.right();
|
||||||
|
_name.resizeToWidth(qMin(nameWidth, _name.naturalWidth()) + marginsAdd);
|
||||||
|
_name.moveToLeft(nameLeft, nameTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A more generic solution would be allowing an optional icon button
|
||||||
|
// for each text button. But currently we use only one, so it is done easily:
|
||||||
|
// There can be primary + secondary + icon buttons. If primary + secondary fit,
|
||||||
|
// then icon is hidden, otherwise secondary is hidden and icon is shown.
|
||||||
|
void CoverWidget::moveAndToggleButtons(int newWiddth) {
|
||||||
|
int buttonLeft = _userpicButton->x() + _userpicButton->width() + st::profileButtonLeft;
|
||||||
|
int buttonsRight = newWiddth - st::profileButtonSkip;
|
||||||
|
for (int i = 0, count = _buttons.size(); i < count; ++i) {
|
||||||
|
auto &button = _buttons.at(i);
|
||||||
|
button.widget->moveToLeft(buttonLeft, st::profileButtonTop);
|
||||||
|
if (button.replacement) {
|
||||||
|
button.replacement->moveToLeft(buttonLeft, st::profileButtonTop);
|
||||||
|
if (buttonLeft + button.widget->width() > buttonsRight) {
|
||||||
|
button.widget->hide();
|
||||||
|
button.replacement->show();
|
||||||
|
buttonLeft += button.replacement->width() + st::profileButtonSkip;
|
||||||
|
} else {
|
||||||
|
button.widget->show();
|
||||||
|
button.replacement->hide();
|
||||||
|
buttonLeft += button.widget->width() + st::profileButtonSkip;
|
||||||
|
}
|
||||||
|
} else if (i == 1 && (buttonLeft + button.widget->width() > buttonsRight)) {
|
||||||
|
// If second button is not fitting.
|
||||||
|
button.widget->hide();
|
||||||
|
} else {
|
||||||
|
button.widget->show();
|
||||||
|
buttonLeft += button.widget->width() + st::profileButtonSkip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::showFinished() {
|
||||||
|
_userpicButton->showFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoverWidget::shareContactButtonShown() const {
|
||||||
|
return _peerUser && (_buttons.size() > 1) && !(_buttons.at(1).widget->isHidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
p.fillRect(e->rect(), st::profileBg);
|
||||||
|
|
||||||
|
p.setFont(st::profileStatusFont);
|
||||||
|
p.setPen(_statusTextIsOnline ? st::profileStatusFgActive : st::profileStatusFg);
|
||||||
|
p.drawTextLeft(_statusPosition.x(), _statusPosition.y(), width(), _statusText);
|
||||||
|
|
||||||
|
paintDivider(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::resizeDropArea() {
|
||||||
|
if (_dropArea) {
|
||||||
|
_dropArea->setGeometry(0, 0, width(), _dividerTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::dropAreaHidden(CoverDropArea *dropArea) {
|
||||||
|
if (_dropArea == dropArea) {
|
||||||
|
_dropArea.destroyDelayed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoverWidget::canEditPhoto() const {
|
||||||
|
if (_peerChat && _peerChat->canEdit()) {
|
||||||
|
return true;
|
||||||
|
} else if (_peerMegagroup && _peerMegagroup->canEditPhoto()) {
|
||||||
|
return true;
|
||||||
|
} else if (_peerChannel && _peerChannel->canEditPhoto()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CoverWidget::mimeDataHasImage(const QMimeData *mimeData) const {
|
||||||
|
if (!mimeData) return false;
|
||||||
|
|
||||||
|
if (mimeData->hasImage()) return true;
|
||||||
|
|
||||||
|
auto uriListFormat = qsl("text/uri-list");
|
||||||
|
if (!mimeData->hasFormat(uriListFormat)) return false;
|
||||||
|
|
||||||
|
const auto &urls = mimeData->urls();
|
||||||
|
if (urls.size() != 1) return false;
|
||||||
|
|
||||||
|
auto &url = urls.at(0);
|
||||||
|
if (!url.isLocalFile()) return false;
|
||||||
|
|
||||||
|
auto file = psConvertFileUrl(url);
|
||||||
|
|
||||||
|
QFileInfo info(file);
|
||||||
|
if (info.isDir()) return false;
|
||||||
|
|
||||||
|
quint64 s = info.size();
|
||||||
|
if (s >= MaxUploadDocumentSize) return false;
|
||||||
|
|
||||||
|
for (auto &ext : cImgExtensions()) {
|
||||||
|
if (file.endsWith(ext, Qt::CaseInsensitive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::dragEnterEvent(QDragEnterEvent *e) {
|
||||||
|
if (!canEditPhoto() || !mimeDataHasImage(e->mimeData())) {
|
||||||
|
e->ignore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_dropArea) {
|
||||||
|
auto title = lang(lng_profile_drop_area_title);
|
||||||
|
QString subtitle;
|
||||||
|
if (_peerChat || _peerMegagroup) {
|
||||||
|
subtitle = lang(lng_profile_drop_area_subtitle);
|
||||||
|
} else {
|
||||||
|
subtitle = lang(lng_profile_drop_area_subtitle_channel);
|
||||||
|
}
|
||||||
|
_dropArea = new CoverDropArea(this, title, subtitle);
|
||||||
|
resizeDropArea();
|
||||||
|
}
|
||||||
|
_dropArea->showAnimated();
|
||||||
|
e->setDropAction(Qt::CopyAction);
|
||||||
|
e->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::dragLeaveEvent(QDragLeaveEvent *e) {
|
||||||
|
if (_dropArea && !_dropArea->hiding()) {
|
||||||
|
_dropArea->hideAnimated(func(this, &CoverWidget::dropAreaHidden));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::dropEvent(QDropEvent *e) {
|
||||||
|
auto mimeData = e->mimeData();
|
||||||
|
|
||||||
|
QImage img;
|
||||||
|
if (mimeData->hasImage()) {
|
||||||
|
img = qvariant_cast<QImage>(mimeData->imageData());
|
||||||
|
} else {
|
||||||
|
const auto &urls = mimeData->urls();
|
||||||
|
if (urls.size() == 1) {
|
||||||
|
auto &url = urls.at(0);
|
||||||
|
if (url.isLocalFile()) {
|
||||||
|
img = App::readImage(psConvertFileUrl(url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_dropArea->hiding()) {
|
||||||
|
_dropArea->hideAnimated(func(this, &CoverWidget::dropAreaHidden));
|
||||||
|
}
|
||||||
|
e->acceptProposedAction();
|
||||||
|
|
||||||
|
showSetPhotoBox(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::paintDivider(Painter &p) {
|
||||||
|
st::profileDividerLeft.paint(p, QPoint(st::lineWidth, _dividerTop), width());
|
||||||
|
|
||||||
|
int toFillLeft = st::lineWidth + st::profileDividerLeft.width();
|
||||||
|
QRect toFill = rtlrect(toFillLeft, _dividerTop, width() - toFillLeft, st::profileDividerFill.height(), width());
|
||||||
|
st::profileDividerFill.fill(p, toFill);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer != _peer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((update.flags & ButtonsUpdateFlags) != 0) {
|
||||||
|
refreshButtons();
|
||||||
|
}
|
||||||
|
if (update.flags & UpdateFlag::NameChanged) {
|
||||||
|
refreshNameText();
|
||||||
|
}
|
||||||
|
if (update.flags & UpdateFlag::UserOnlineChanged) {
|
||||||
|
refreshStatusText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::refreshNameText() {
|
||||||
|
_name.setText(App::peerName(_peer));
|
||||||
|
refreshNameGeometry(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::refreshStatusText() {
|
||||||
|
int currentTime = unixtime();
|
||||||
|
if (_peerUser) {
|
||||||
|
_statusText = App::onlineText(_peerUser, currentTime, true);
|
||||||
|
_statusTextIsOnline = App::onlineColorUse(_peerUser, currentTime);
|
||||||
|
} else if (_peerChat && _peerChat->amIn()) {
|
||||||
|
int fullCount = qMax(_peerChat->count, _peerChat->participants.size());
|
||||||
|
if (_onlineCount > 0 && _onlineCount <= fullCount) {
|
||||||
|
_statusText = lng_chat_status_members_online(lt_count, fullCount, lt_count_online, _onlineCount);
|
||||||
|
} else {
|
||||||
|
_statusText = lng_chat_status_members(lt_count, _peerChat->count);
|
||||||
|
}
|
||||||
|
} else if (_peerChannel) {
|
||||||
|
int fullCount = _peerChannel->membersCount();
|
||||||
|
if (_onlineCount > 0 && _onlineCount <= fullCount) {
|
||||||
|
_statusText = lng_chat_status_members_online(lt_count, fullCount, lt_count_online, _onlineCount);
|
||||||
|
} else if (fullCount > 0 ) {
|
||||||
|
_statusText = lng_chat_status_members(lt_count, _peerChannel->membersCount());
|
||||||
|
} else {
|
||||||
|
_statusText = lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_statusText = lang(lng_chat_status_unaccessible);
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::refreshButtons() {
|
||||||
|
clearButtons();
|
||||||
|
if (_peerUser) {
|
||||||
|
setUserButtons();
|
||||||
|
} else if (_peerChat) {
|
||||||
|
setChatButtons();
|
||||||
|
} else if (_peerMegagroup) {
|
||||||
|
setMegagroupButtons();
|
||||||
|
} else if (_peerChannel) {
|
||||||
|
setChannelButtons();
|
||||||
|
}
|
||||||
|
resizeToWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::setUserButtons() {
|
||||||
|
addButton(lang(lng_profile_send_message), SLOT(onSendMessage()));
|
||||||
|
if (_peerUser->canShareThisContact()) {
|
||||||
|
addButton(lang(lng_profile_share_contact), SLOT(onShareContact()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::setChatButtons() {
|
||||||
|
if (_peerChat->canEdit()) {
|
||||||
|
addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||||
|
addButton(lang(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::setMegagroupButtons() {
|
||||||
|
if (_peerMegagroup->amIn()) {
|
||||||
|
if (_peerMegagroup->canEditPhoto()) {
|
||||||
|
addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addButton(lang(lng_profile_join_channel), SLOT(onJoin()));
|
||||||
|
}
|
||||||
|
if (_peerMegagroup->canAddMembers()) {
|
||||||
|
addButton(lang(lng_profile_add_participant), SLOT(onAddMember()), &st::profileAddMemberButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::setChannelButtons() {
|
||||||
|
if (_peerChannel->amCreator()) {
|
||||||
|
addButton(lang(lng_profile_set_group_photo), SLOT(onSetPhoto()));
|
||||||
|
} else if (_peerChannel->amIn()) {
|
||||||
|
addButton(lang(lng_profile_view_channel), SLOT(onViewChannel()));
|
||||||
|
} else {
|
||||||
|
addButton(lang(lng_profile_join_channel), SLOT(onJoin()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::clearButtons() {
|
||||||
|
auto buttons = createAndSwap(_buttons);
|
||||||
|
for_const (auto button, buttons) {
|
||||||
|
delete button.widget;
|
||||||
|
delete button.replacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::addButton(const QString &text, const char *slot, const style::BoxButton *replacementStyle) {
|
||||||
|
auto &buttonStyle = _buttons.isEmpty() ? st::profilePrimaryButton : st::profileSecondaryButton;
|
||||||
|
auto button = new Ui::RoundButton(this, text, buttonStyle);
|
||||||
|
connect(button, SIGNAL(clicked()), this, slot);
|
||||||
|
button->show();
|
||||||
|
|
||||||
|
auto replacement = replacementStyle ? new Ui::RoundButton(this, QString(), *replacementStyle) : nullptr;
|
||||||
|
if (replacement) {
|
||||||
|
connect(replacement, SIGNAL(clicked()), this, slot);
|
||||||
|
replacement->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
_buttons.push_back({ button, replacement });
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onOnlineCountUpdated(int onlineCount) {
|
||||||
|
_onlineCount = onlineCount;
|
||||||
|
refreshStatusText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onSendMessage() {
|
||||||
|
Ui::showPeerHistory(_peer, ShowAtUnreadMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onShareContact() {
|
||||||
|
App::main()->shareContactLayer(_peerUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onSetPhoto() {
|
||||||
|
QStringList imgExtensions(cImgExtensions());
|
||||||
|
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;All files (*.*)"));
|
||||||
|
|
||||||
|
_setPhotoFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) {
|
||||||
|
if (_setPhotoFileQueryId != update.queryId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_setPhotoFileQueryId = 0;
|
||||||
|
|
||||||
|
if (update.filePaths.isEmpty() && update.remoteContent.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage img;
|
||||||
|
if (!update.remoteContent.isEmpty()) {
|
||||||
|
img = App::readImage(update.remoteContent);
|
||||||
|
} else {
|
||||||
|
img = App::readImage(update.filePaths.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
showSetPhotoBox(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::showSetPhotoBox(const QImage &img) {
|
||||||
|
if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) {
|
||||||
|
Ui::showLayer(new InformBox(lang(lng_bad_photo)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto box = new PhotoCropBox(img, _peer);
|
||||||
|
connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onAddMember() {
|
||||||
|
if (_peerChat) {
|
||||||
|
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
|
||||||
|
Ui::showLayer(new ConvertToSupergroupBox(_peerChat));
|
||||||
|
} else {
|
||||||
|
Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
|
||||||
|
}
|
||||||
|
} else if (_peerChannel && _peerChannel->mgInfo) {
|
||||||
|
MembersAlreadyIn already;
|
||||||
|
for_const (auto user, _peerChannel->mgInfo->lastParticipants) {
|
||||||
|
already.insert(user);
|
||||||
|
}
|
||||||
|
Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onJoin() {
|
||||||
|
if (!_peerChannel) return;
|
||||||
|
|
||||||
|
App::api()->joinChannel(_peerChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverWidget::onViewChannel() {
|
||||||
|
Ui::showPeerHistory(_peer, ShowAtUnreadMsgId);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
137
Telegram/SourceFiles/profile/profile_cover.h
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
#include "ui/filedialog.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class RoundButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
struct PeerUpdate;
|
||||||
|
} // namespace Notify
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class BackButton;
|
||||||
|
class UserpicButton;
|
||||||
|
class CoverDropArea;
|
||||||
|
|
||||||
|
class CoverWidget final : public TWidget, public Notify::Observer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CoverWidget(QWidget *parent, PeerData *peer);
|
||||||
|
|
||||||
|
// Count new height for width=newWidth and resize to it.
|
||||||
|
void resizeToWidth(int newWidth);
|
||||||
|
|
||||||
|
void showFinished();
|
||||||
|
|
||||||
|
// Profile fixed top bar should use this flag to decide
|
||||||
|
// if it shows "Share contact" button or not.
|
||||||
|
// It should show it only if it is hidden in the cover.
|
||||||
|
bool shareContactButtonShown() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onOnlineCountUpdated(int onlineCount);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onPhotoShow();
|
||||||
|
|
||||||
|
void onSendMessage();
|
||||||
|
void onShareContact();
|
||||||
|
void onSetPhoto();
|
||||||
|
void onAddMember();
|
||||||
|
void onJoin();
|
||||||
|
void onViewChannel();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void dragEnterEvent(QDragEnterEvent *e) override;
|
||||||
|
void dragLeaveEvent(QDragLeaveEvent *e) override;
|
||||||
|
void dropEvent(QDropEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Observed notifications.
|
||||||
|
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||||
|
void notifyFileQueryUpdated(const FileDialog::QueryUpdate &update);
|
||||||
|
|
||||||
|
// Counts userpic button left offset for a new widget width.
|
||||||
|
int countPhotoLeft(int newWidth) const;
|
||||||
|
PhotoData *validatePhoto() const;
|
||||||
|
|
||||||
|
void refreshNameGeometry(int newWidth);
|
||||||
|
void moveAndToggleButtons(int newWiddth);
|
||||||
|
void refreshNameText();
|
||||||
|
void refreshStatusText();
|
||||||
|
|
||||||
|
void refreshButtons();
|
||||||
|
void setUserButtons();
|
||||||
|
void setChatButtons();
|
||||||
|
void setMegagroupButtons();
|
||||||
|
void setChannelButtons();
|
||||||
|
|
||||||
|
void clearButtons();
|
||||||
|
void addButton(const QString &text, const char *slot, const style::BoxButton *replacementStyle = nullptr);
|
||||||
|
|
||||||
|
void paintDivider(Painter &p);
|
||||||
|
|
||||||
|
bool canEditPhoto() const;
|
||||||
|
void showSetPhotoBox(const QImage &img);
|
||||||
|
void resizeDropArea();
|
||||||
|
void dropAreaHidden(CoverDropArea *dropArea);
|
||||||
|
bool mimeDataHasImage(const QMimeData *mimeData) const;
|
||||||
|
|
||||||
|
PeerData *_peer;
|
||||||
|
UserData *_peerUser;
|
||||||
|
ChatData *_peerChat;
|
||||||
|
ChannelData *_peerChannel;
|
||||||
|
ChannelData *_peerMegagroup;
|
||||||
|
|
||||||
|
ChildWidget<UserpicButton> _userpicButton;
|
||||||
|
ChildWidget<CoverDropArea> _dropArea = { nullptr };
|
||||||
|
|
||||||
|
FlatLabel _name;
|
||||||
|
|
||||||
|
QPoint _statusPosition;
|
||||||
|
QString _statusText;
|
||||||
|
bool _statusTextIsOnline = false;
|
||||||
|
|
||||||
|
struct Button {
|
||||||
|
Ui::RoundButton *widget;
|
||||||
|
Ui::RoundButton *replacement;
|
||||||
|
};
|
||||||
|
QList<Button> _buttons;
|
||||||
|
|
||||||
|
int _photoLeft = 0; // Caching countPhotoLeft() result.
|
||||||
|
int _dividerTop = 0;
|
||||||
|
|
||||||
|
int _onlineCount = 0;
|
||||||
|
|
||||||
|
FileDialog::QueryId _setPhotoFileQueryId = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Profile
|
99
Telegram/SourceFiles/profile/profile_cover_drop_area.cpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_cover_drop_area.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
CoverDropArea::CoverDropArea(QWidget *parent, const QString &title, const QString &subtitle) : TWidget(parent)
|
||||||
|
, _title(title)
|
||||||
|
, _subtitle(subtitle)
|
||||||
|
, _titleWidth(st::profileDropAreaTitleFont->width(_title))
|
||||||
|
, _subtitleWidth(st::profileDropAreaSubtitleFont->width(_subtitle)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverDropArea::showAnimated() {
|
||||||
|
show();
|
||||||
|
_hiding = false;
|
||||||
|
setupAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverDropArea::hideAnimated(HideFinishCallback &&callback) {
|
||||||
|
_hideFinishCallback = std_::move(callback);
|
||||||
|
_hiding = true;
|
||||||
|
setupAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverDropArea::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
if (_a_appearance.animating(getms())) {
|
||||||
|
p.setOpacity(_a_appearance.current());
|
||||||
|
p.drawPixmap(0, 0, _cache);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_cache.isNull()) {
|
||||||
|
_cache = QPixmap();
|
||||||
|
if (_hiding) {
|
||||||
|
hide();
|
||||||
|
_hideFinishCallback.call(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.fillRect(e->rect(), st::profileDropAreaBg);
|
||||||
|
|
||||||
|
if (width() < st::profileDropAreaPadding.left() + st::profileDropAreaPadding.right()) return;
|
||||||
|
if (height() < st::profileDropAreaPadding.top() + st::profileDropAreaPadding.bottom()) return;
|
||||||
|
|
||||||
|
auto border = st::profileDropAreaBorderWidth;
|
||||||
|
auto &borderFg = st::profileDropAreaBorderFg;
|
||||||
|
auto inner = rect().marginsRemoved(st::profileDropAreaPadding);
|
||||||
|
p.fillRect(inner.x(), inner.y(), inner.width(), border, borderFg);
|
||||||
|
p.fillRect(inner.x(), inner.y() + inner.height() - border, inner.width(), border, borderFg);
|
||||||
|
p.fillRect(inner.x(), inner.y() + border, border, inner.height() - 2 * border, borderFg);
|
||||||
|
p.fillRect(inner.x() + inner.width() - border, inner.y() + border, border, inner.height() - 2 * border, borderFg);
|
||||||
|
|
||||||
|
int titleLeft = inner.x() + (inner.width() - _titleWidth) / 2;
|
||||||
|
int titleTop = inner.y() + st::profileDropAreaTitleTop + st::profileDropAreaTitleFont->ascent;
|
||||||
|
p.setFont(st::profileDropAreaTitleFont);
|
||||||
|
p.setPen(st::profileDropAreaFg);
|
||||||
|
p.drawText(titleLeft, titleTop, _title);
|
||||||
|
|
||||||
|
int subtitleLeft = inner.x() + (inner.width() - _subtitleWidth) / 2;
|
||||||
|
int subtitleTop = inner.y() + st::profileDropAreaSubtitleTop + st::profileDropAreaSubtitleFont->ascent;
|
||||||
|
p.setFont(st::profileDropAreaSubtitleFont);
|
||||||
|
p.setPen(st::profileDropAreaFg);
|
||||||
|
p.drawText(subtitleLeft, subtitleTop, _subtitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CoverDropArea::setupAnimation() {
|
||||||
|
if (_cache.isNull()) {
|
||||||
|
_cache = myGrab(this);
|
||||||
|
}
|
||||||
|
auto from = _hiding ? 1. : 0., to = _hiding ? 0. : 1.;
|
||||||
|
START_ANIMATION(_a_appearance, func(this, &CoverDropArea::refreshCallback), from, to, st::profileDropAreaDuration, anim::linear);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
57
Telegram/SourceFiles/profile/profile_cover_drop_area.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class CoverDropArea : public TWidget {
|
||||||
|
public:
|
||||||
|
CoverDropArea(QWidget *parent, const QString &title, const QString &subtitle);
|
||||||
|
|
||||||
|
void showAnimated();
|
||||||
|
|
||||||
|
using HideFinishCallback = Function<void, CoverDropArea*>;
|
||||||
|
void hideAnimated(HideFinishCallback &&callback);
|
||||||
|
|
||||||
|
bool hiding() const {
|
||||||
|
return _hiding;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refreshCallback() {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
void setupAnimation();
|
||||||
|
|
||||||
|
QString _title, _subtitle;
|
||||||
|
int _titleWidth, _subtitleWidth;
|
||||||
|
|
||||||
|
QPixmap _cache;
|
||||||
|
FloatAnimation _a_appearance;
|
||||||
|
bool _hiding = false;
|
||||||
|
HideFinishCallback _hideFinishCallback;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Profile
|
284
Telegram/SourceFiles/profile/profile_fixed_bar.cpp
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_fixed_bar.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "lang.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "boxes/addcontactbox.h"
|
||||||
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class BackButton final : public Button {
|
||||||
|
public:
|
||||||
|
BackButton(QWidget *parent) : Button(parent) {
|
||||||
|
setCursor(style::cur_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resizeToWidth(int newWidth) {
|
||||||
|
resize(newWidth, st::profileTopBarHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
p.fillRect(e->rect(), st::profileBg);
|
||||||
|
st::profileTopBarBackIcon.paint(p, st::profileTopBarBackIconPosition, width());
|
||||||
|
|
||||||
|
p.setFont(st::profileTopBarBackFont);
|
||||||
|
p.setPen(st::profileTopBarBackFg);
|
||||||
|
p.drawTextLeft(st::profileTopBarBackPosition.x(), st::profileTopBarBackPosition.y(), width(), lang(lng_menu_back));
|
||||||
|
}
|
||||||
|
void onStateChanged(int oldState, ButtonStateChangeSource source) override {
|
||||||
|
if ((_state & Button::StateDown) && !(oldState & Button::StateDown)) {
|
||||||
|
emit clicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||||
|
const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact
|
||||||
|
| UpdateFlag::UserIsContact
|
||||||
|
| UpdateFlag::ChatCanEdit
|
||||||
|
| UpdateFlag::ChannelAmEditor;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FixedBar::FixedBar(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||||
|
, _peer(peer)
|
||||||
|
, _peerUser(peer->asUser())
|
||||||
|
, _peerChat(peer->asChat())
|
||||||
|
, _peerChannel(peer->asChannel())
|
||||||
|
, _peerMegagroup(peer->isMegagroup() ? _peerChannel : nullptr)
|
||||||
|
, _backButton(this) {
|
||||||
|
_backButton->moveToLeft(0, 0);
|
||||||
|
connect(_backButton, SIGNAL(clicked()), this, SLOT(onBack()));
|
||||||
|
|
||||||
|
auto observeEvents = ButtonsUpdateFlags
|
||||||
|
| UpdateFlag::MigrationChanged;
|
||||||
|
Notify::registerPeerObserver(observeEvents, this, &FixedBar::notifyPeerUpdate);
|
||||||
|
|
||||||
|
refreshRightActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::notifyPeerUpdate(const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer != _peer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((update.flags & ButtonsUpdateFlags) != 0) {
|
||||||
|
refreshRightActions();
|
||||||
|
}
|
||||||
|
if (update.flags & UpdateFlag::MigrationChanged) {
|
||||||
|
if (_peerChat && _peerChat->migrateTo()) {
|
||||||
|
auto channel = _peerChat->migrateTo();
|
||||||
|
onBack();
|
||||||
|
Ui::showPeerProfile(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::refreshRightActions() {
|
||||||
|
_currentAction = 0;
|
||||||
|
if (_peerUser) {
|
||||||
|
setUserActions();
|
||||||
|
} else if (_peerChat) {
|
||||||
|
setChatActions();
|
||||||
|
} else if (_peerMegagroup) {
|
||||||
|
setMegagroupActions();
|
||||||
|
} else if (_peerChannel) {
|
||||||
|
setChannelActions();
|
||||||
|
}
|
||||||
|
while (_rightActions.size() > _currentAction) {
|
||||||
|
delete _rightActions.back().button;
|
||||||
|
_rightActions.pop_back();
|
||||||
|
}
|
||||||
|
resizeToWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::setUserActions() {
|
||||||
|
if (_peerUser->canShareThisContact()) {
|
||||||
|
addRightAction(RightActionType::ShareContact, lang(lng_profile_top_bar_share_contact), SLOT(onShareContact()));
|
||||||
|
}
|
||||||
|
if (_peerUser->isContact()) {
|
||||||
|
addRightAction(RightActionType::EditContact, lang(lng_profile_edit_contact), SLOT(onEditContact()));
|
||||||
|
addRightAction(RightActionType::DeleteContact, lang(lng_profile_delete_contact), SLOT(onDeleteContact()));
|
||||||
|
} else if (_peerUser->canAddContact()) {
|
||||||
|
addRightAction(RightActionType::AddContact, lang(lng_profile_add_contact), SLOT(onAddContact()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::setChatActions() {
|
||||||
|
if (_peerChat->canEdit()) {
|
||||||
|
addRightAction(RightActionType::EditGroup, lang(lng_profile_edit_contact), SLOT(onEditGroup()));
|
||||||
|
}
|
||||||
|
addRightAction(RightActionType::LeaveGroup, lang(lng_profile_delete_and_exit), SLOT(onLeaveGroup()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::setMegagroupActions() {
|
||||||
|
if (_peerMegagroup->amCreator() || _peerMegagroup->amEditor()) {
|
||||||
|
addRightAction(RightActionType::EditChannel, lang(lng_profile_edit_contact), SLOT(onEditChannel()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::setChannelActions() {
|
||||||
|
if (_peerChannel->amCreator()) {
|
||||||
|
addRightAction(RightActionType::EditChannel, lang(lng_profile_edit_contact), SLOT(onEditChannel()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::addRightAction(RightActionType type, const QString &text, const char *slot) {
|
||||||
|
if (_rightActions.size() > _currentAction) {
|
||||||
|
if (_rightActions.at(_currentAction).type == type) {
|
||||||
|
++_currentAction;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t_assert(_rightActions.size() == _currentAction);
|
||||||
|
_rightActions.push_back(RightAction());
|
||||||
|
}
|
||||||
|
_rightActions[_currentAction].type = type;
|
||||||
|
delete _rightActions[_currentAction].button;
|
||||||
|
_rightActions[_currentAction].button = new FlatButton(this, text, st::profileFixedBarButton);
|
||||||
|
connect(_rightActions[_currentAction].button, SIGNAL(clicked()), this, slot);
|
||||||
|
bool showButton = !_animatingMode && (type != RightActionType::ShareContact || !_hideShareContactButton);
|
||||||
|
_rightActions[_currentAction].button->setVisible(showButton);
|
||||||
|
++_currentAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onBack() {
|
||||||
|
App::main()->showBackFromStack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onEditChannel() {
|
||||||
|
Ui::showLayer(new EditChannelBox(_peerMegagroup ? _peerMegagroup : _peerChannel));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onEditGroup() {
|
||||||
|
Ui::showLayer(new EditNameTitleBox(_peerChat));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onAddContact() {
|
||||||
|
auto firstName = _peerUser->firstName;
|
||||||
|
auto lastName = _peerUser->lastName;
|
||||||
|
auto phone = _peerUser->phone().isEmpty() ? App::phoneFromSharedContact(peerToUser(_peer->id)) : _peerUser->phone();
|
||||||
|
Ui::showLayer(new AddContactBox(firstName, lastName, phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onEditContact() {
|
||||||
|
Ui::showLayer(new AddContactBox(_peerUser));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onShareContact() {
|
||||||
|
App::main()->shareContactLayer(_peerUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onDeleteContact() {
|
||||||
|
ConfirmBox *box = new ConfirmBox(lng_sure_delete_contact(lt_contact, App::peerName(_peerUser)), lang(lng_box_delete));
|
||||||
|
connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteContactSure()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onDeleteContactSure() {
|
||||||
|
Ui::showChatsList();
|
||||||
|
Ui::hideLayer();
|
||||||
|
MTP::send(MTPcontacts_DeleteContact(_peerUser->inputUser), App::main()->rpcDone(&MainWidget::deletedContact, _peerUser));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onLeaveGroup() {
|
||||||
|
ConfirmBox *box = new ConfirmBox(lng_sure_delete_and_exit(lt_group, App::peerName(_peerChat)), lang(lng_box_leave), st::attentionBoxButton);
|
||||||
|
connect(box, SIGNAL(confirmed()), this, SLOT(onLeaveGroupSure()));
|
||||||
|
Ui::showLayer(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::onLeaveGroupSure() {
|
||||||
|
Ui::showChatsList();
|
||||||
|
Ui::hideLayer();
|
||||||
|
App::main()->deleteAndExit(_peerChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::resizeToWidth(int newWidth) {
|
||||||
|
int newHeight = 0;
|
||||||
|
|
||||||
|
int buttonLeft = newWidth;
|
||||||
|
for (auto i = _rightActions.cend(), b = _rightActions.cbegin(); i != b;) {
|
||||||
|
--i;
|
||||||
|
buttonLeft -= i->button->width();
|
||||||
|
i->button->moveToLeft(buttonLeft, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_backButton->resizeToWidth(newWidth);
|
||||||
|
_backButton->moveToLeft(0, 0);
|
||||||
|
newHeight += _backButton->height();
|
||||||
|
|
||||||
|
resize(newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::setAnimatingMode(bool enabled) {
|
||||||
|
if (_animatingMode != enabled) {
|
||||||
|
_animatingMode = enabled;
|
||||||
|
setCursor(_animatingMode ? style::cur_pointer : style::cur_default);
|
||||||
|
if (_animatingMode) {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent, false);
|
||||||
|
hideChildren();
|
||||||
|
} else {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
showChildren();
|
||||||
|
if (_hideShareContactButton) {
|
||||||
|
applyHideShareContactButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::setHideShareContactButton(bool hideButton) {
|
||||||
|
_hideShareContactButton = hideButton;
|
||||||
|
if (!_animatingMode) {
|
||||||
|
applyHideShareContactButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::applyHideShareContactButton() {
|
||||||
|
for_const (auto &action, _rightActions) {
|
||||||
|
if (action.type == RightActionType::ShareContact) {
|
||||||
|
action.button->setVisible(!_hideShareContactButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixedBar::mousePressEvent(QMouseEvent *e) {
|
||||||
|
if (e->button() == Qt::LeftButton) {
|
||||||
|
onBack();
|
||||||
|
} else {
|
||||||
|
TWidget::mousePressEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
108
Telegram/SourceFiles/profile/profile_fixed_bar.h
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/observer.h"
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
struct PeerUpdate;
|
||||||
|
} // namespace Notify
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class BackButton;
|
||||||
|
|
||||||
|
class FixedBar final : public TWidget, public Notify::Observer {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FixedBar(QWidget *parent, PeerData *peer);
|
||||||
|
|
||||||
|
void resizeToWidth(int newWidth);
|
||||||
|
|
||||||
|
// When animating mode is enabled the content is hidden and the
|
||||||
|
// whole fixed bar acts like a back button.
|
||||||
|
void setAnimatingMode(bool enabled);
|
||||||
|
|
||||||
|
// The "Share contact" button should be hidden if it is shown in the profile cover.
|
||||||
|
void setHideShareContactButton(bool hideButton);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onBack();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onEditChannel();
|
||||||
|
void onEditGroup();
|
||||||
|
void onAddContact();
|
||||||
|
void onEditContact();
|
||||||
|
void onShareContact();
|
||||||
|
void onDeleteContact();
|
||||||
|
void onDeleteContactSure();
|
||||||
|
void onLeaveGroup();
|
||||||
|
void onLeaveGroupSure();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void notifyPeerUpdate(const Notify::PeerUpdate &update);
|
||||||
|
|
||||||
|
void refreshRightActions();
|
||||||
|
void setUserActions();
|
||||||
|
void setChatActions();
|
||||||
|
void setMegagroupActions();
|
||||||
|
void setChannelActions();
|
||||||
|
|
||||||
|
enum class RightActionType {
|
||||||
|
None,
|
||||||
|
EditChannel,
|
||||||
|
EditGroup,
|
||||||
|
LeaveGroup,
|
||||||
|
AddContact,
|
||||||
|
EditContact,
|
||||||
|
DeleteContact,
|
||||||
|
ShareContact,
|
||||||
|
};
|
||||||
|
|
||||||
|
void addRightAction(RightActionType type, const QString &text, const char *slot);
|
||||||
|
void applyHideShareContactButton();
|
||||||
|
|
||||||
|
PeerData *_peer;
|
||||||
|
UserData *_peerUser;
|
||||||
|
ChatData *_peerChat;
|
||||||
|
ChannelData *_peerChannel;
|
||||||
|
ChannelData *_peerMegagroup;
|
||||||
|
|
||||||
|
ChildWidget<BackButton> _backButton;
|
||||||
|
|
||||||
|
int _currentAction = 0;
|
||||||
|
struct RightAction {
|
||||||
|
RightActionType type = RightActionType::None;
|
||||||
|
FlatButton *button = nullptr;
|
||||||
|
};
|
||||||
|
QList<RightAction> _rightActions;
|
||||||
|
|
||||||
|
bool _animatingMode = false;
|
||||||
|
bool _hideShareContactButton = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Profile
|
219
Telegram/SourceFiles/profile/profile_info_widget.cpp
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_info_widget.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
#include "lang.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
using UpdateFlag = Notify::PeerUpdate::Flag;
|
||||||
|
|
||||||
|
InfoWidget::InfoWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_info_section)) {
|
||||||
|
auto observeEvents = UpdateFlag::AboutChanged
|
||||||
|
| UpdateFlag::UsernameChanged
|
||||||
|
| UpdateFlag::UserPhoneChanged
|
||||||
|
| UpdateFlag::UserCanShareContact;
|
||||||
|
Notify::registerPeerObserver(observeEvents, this, &InfoWidget::notifyPeerUpdated);
|
||||||
|
|
||||||
|
refreshLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer != peer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.flags & UpdateFlag::AboutChanged) {
|
||||||
|
refreshAbout();
|
||||||
|
}
|
||||||
|
if (update.flags & UpdateFlag::UsernameChanged) {
|
||||||
|
refreshUsername();
|
||||||
|
refreshChannelLink();
|
||||||
|
}
|
||||||
|
if (update.flags & (UpdateFlag::UserPhoneChanged | UpdateFlag::UserCanShareContact)) {
|
||||||
|
refreshMobileNumber();
|
||||||
|
}
|
||||||
|
refreshVisibility();
|
||||||
|
|
||||||
|
contentSizeUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
int InfoWidget::resizeGetHeight(int newWidth) {
|
||||||
|
int newHeight = contentTop();
|
||||||
|
|
||||||
|
int marginLeft = st::profileBlockTextPart.margin.left();
|
||||||
|
int marginRight = st::profileBlockTextPart.margin.right();
|
||||||
|
int left = st::profileBlockTitlePosition.x();
|
||||||
|
if (_about) {
|
||||||
|
int textWidth = _about->naturalWidth();
|
||||||
|
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||||
|
int maxWidth = st::msgMaxWidth;
|
||||||
|
accumulate_min(textWidth, availableWidth);
|
||||||
|
accumulate_min(textWidth, st::msgMaxWidth);
|
||||||
|
_about->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||||
|
_about->moveToLeft(left - marginLeft, newHeight - st::profileBlockTextPart.margin.top());
|
||||||
|
newHeight += _about->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto moveLabeledText = [&newHeight, left, newWidth, marginLeft, marginRight](FlatLabel *label, FlatLabel *text, FlatLabel *shortText) {
|
||||||
|
if (!label) return;
|
||||||
|
|
||||||
|
label->moveToLeft(left, newHeight);
|
||||||
|
int textLeft = left + label->width() + st::normalFont->spacew;
|
||||||
|
int textWidth = text->naturalWidth();
|
||||||
|
int availableWidth = newWidth - textLeft - st::profileBlockMarginRight;
|
||||||
|
bool doesNotFit = (textWidth > availableWidth);
|
||||||
|
accumulate_min(textWidth, availableWidth);
|
||||||
|
accumulate_min(textWidth, st::msgMaxWidth);
|
||||||
|
text->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||||
|
text->moveToLeft(textLeft - marginLeft, newHeight - st::profileBlockOneLineTextPart.margin.top());
|
||||||
|
if (shortText) {
|
||||||
|
shortText->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||||
|
shortText->moveToLeft(textLeft - marginLeft, newHeight - st::profileBlockOneLineTextPart.margin.top());
|
||||||
|
if (doesNotFit) {
|
||||||
|
shortText->show();
|
||||||
|
text->hide();
|
||||||
|
} else {
|
||||||
|
shortText->hide();
|
||||||
|
text->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newHeight += label->height() + st::profileBlockOneLineSkip;
|
||||||
|
};
|
||||||
|
moveLabeledText(_channelLinkLabel, _channelLink, _channelLinkShort);
|
||||||
|
moveLabeledText(_mobileNumberLabel, _mobileNumber, nullptr);
|
||||||
|
moveLabeledText(_usernameLabel, _username, nullptr);
|
||||||
|
|
||||||
|
newHeight += st::profileBlockMarginBottom;
|
||||||
|
return newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::leaveEvent(QEvent *e) {
|
||||||
|
BotCommandClickHandler::setPeerForCommand(nullptr);
|
||||||
|
BotCommandClickHandler::setBotForCommand(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::refreshLabels() {
|
||||||
|
refreshAbout();
|
||||||
|
refreshMobileNumber();
|
||||||
|
refreshUsername();
|
||||||
|
refreshChannelLink();
|
||||||
|
|
||||||
|
refreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::refreshVisibility() {
|
||||||
|
setVisible(_about || _mobileNumber || _username || _channelLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::refreshAbout() {
|
||||||
|
auto getAboutText = [this]() -> QString {
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
return user->about();
|
||||||
|
} else if (auto channel = peer()->asChannel()) {
|
||||||
|
return channel->about();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
};
|
||||||
|
|
||||||
|
_about.destroy();
|
||||||
|
auto aboutText = getAboutText();
|
||||||
|
if (!aboutText.isEmpty()) {
|
||||||
|
_about = new FlatLabel(this, st::profileBlockTextPart);
|
||||||
|
_about->show();
|
||||||
|
|
||||||
|
EntitiesInText aboutEntities;
|
||||||
|
textParseEntities(aboutText, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands, &aboutEntities);
|
||||||
|
_about->setMarkedText({ aboutText, aboutEntities });
|
||||||
|
_about->setSelectable(true);
|
||||||
|
_about->setClickHandlerHook(func(this, &InfoWidget::aboutClickHandlerHook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InfoWidget::aboutClickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||||
|
BotCommandClickHandler::setPeerForCommand(peer());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::refreshMobileNumber() {
|
||||||
|
TextWithEntities phoneText;
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
if (!user->phone().isEmpty()) {
|
||||||
|
phoneText.text = App::formatPhone(user->phone());
|
||||||
|
} else {
|
||||||
|
phoneText.text = App::phoneFromSharedContact(peerToUser(user->id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLabeledText(&_mobileNumberLabel, lang(lng_profile_mobile_number), &_mobileNumber, phoneText, lang(lng_profile_copy_phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::refreshUsername() {
|
||||||
|
TextWithEntities usernameText;
|
||||||
|
if (auto user = peer()->asUser()) {
|
||||||
|
if (!user->username.isEmpty()) {
|
||||||
|
usernameText.text = '@' + user->username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLabeledText(&_usernameLabel, lang(lng_profile_username), &_username, usernameText, lang(lng_context_copy_mention));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::refreshChannelLink() {
|
||||||
|
TextWithEntities channelLinkText;
|
||||||
|
TextWithEntities channelLinkTextShort;
|
||||||
|
if (auto channel = peer()->asChannel()) {
|
||||||
|
if (!channel->username.isEmpty()) {
|
||||||
|
channelLinkText.text = qsl("https://telegram.me/") + channel->username;
|
||||||
|
channelLinkText.entities.push_back(EntityInText(EntityInTextUrl, 0, channelLinkText.text.size()));
|
||||||
|
channelLinkTextShort.text = qsl("telegram.me/") + channel->username;
|
||||||
|
channelLinkTextShort.entities.push_back(EntityInText(EntityInTextCustomUrl, 0, channelLinkTextShort.text.size(), qsl("https://telegram.me/") + channel->username));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLabeledText(nullptr, lang(lng_profile_link), &_channelLink, channelLinkText, QString());
|
||||||
|
setLabeledText(&_channelLinkLabel, lang(lng_profile_link), &_channelLinkShort, channelLinkTextShort, QString());
|
||||||
|
if (_channelLinkShort) {
|
||||||
|
_channelLinkShort->setExpandLinksMode(ExpandLinksUrlOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InfoWidget::setLabeledText(ChildWidget<FlatLabel> *labelWidget, const QString &label,
|
||||||
|
ChildWidget<FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString ©Text) {
|
||||||
|
if (labelWidget) labelWidget->destroy();
|
||||||
|
textWidget->destroy();
|
||||||
|
if (textWithEntities.text.isEmpty()) return;
|
||||||
|
|
||||||
|
if (labelWidget) {
|
||||||
|
*labelWidget = new FlatLabel(this, label, FlatLabel::InitType::Simple, st::profileBlockLabel);
|
||||||
|
(*labelWidget)->show();
|
||||||
|
}
|
||||||
|
*textWidget = new FlatLabel(this, QString(), FlatLabel::InitType::Simple, st::profileBlockOneLineTextPart);
|
||||||
|
(*textWidget)->show();
|
||||||
|
(*textWidget)->setMarkedText(textWithEntities);
|
||||||
|
(*textWidget)->setContextCopyText(copyText);
|
||||||
|
(*textWidget)->setSelectable(true);
|
||||||
|
(*textWidget)->setDoubleClickSelectsParagraph(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
71
Telegram/SourceFiles/profile/profile_info_widget.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "profile/profile_block_widget.h"
|
||||||
|
|
||||||
|
class FlatLabel;
|
||||||
|
|
||||||
|
namespace Notify {
|
||||||
|
struct PeerUpdate;
|
||||||
|
} // namespace Notify
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class InfoWidget : public BlockWidget {
|
||||||
|
public:
|
||||||
|
InfoWidget(QWidget *parent, PeerData *peer);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Resizes content and counts natural widget height for the desired width.
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
void leaveEvent(QEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Observed notifications.
|
||||||
|
void notifyPeerUpdated(const Notify::PeerUpdate &update);
|
||||||
|
|
||||||
|
void refreshLabels();
|
||||||
|
void refreshAbout();
|
||||||
|
void refreshMobileNumber();
|
||||||
|
void refreshUsername();
|
||||||
|
void refreshChannelLink();
|
||||||
|
void refreshVisibility();
|
||||||
|
|
||||||
|
bool aboutClickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button);
|
||||||
|
|
||||||
|
// labelWidget may be nullptr.
|
||||||
|
void setLabeledText(ChildWidget<FlatLabel> *labelWidget, const QString &label,
|
||||||
|
ChildWidget<FlatLabel> *textWidget, const TextWithEntities &textWithEntities, const QString ©Text);
|
||||||
|
|
||||||
|
ChildWidget<FlatLabel> _about = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _channelLinkLabel = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _channelLink = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _channelLinkShort = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _mobileNumberLabel = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _mobileNumber = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _usernameLabel = { nullptr };
|
||||||
|
ChildWidget<FlatLabel> _username = { nullptr };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Profile
|
257
Telegram/SourceFiles/profile/profile_inner_widget.cpp
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_inner_widget.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "profile/profile_cover.h"
|
||||||
|
#include "profile/profile_info_widget.h"
|
||||||
|
#include "profile/profile_settings_widget.h"
|
||||||
|
#include "profile/profile_invite_link_widget.h"
|
||||||
|
#include "profile/profile_shared_media_widget.h"
|
||||||
|
#include "profile/profile_actions_widget.h"
|
||||||
|
#include "profile/profile_members_widget.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
InnerWidget::InnerWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
|
||||||
|
, _peer(peer)
|
||||||
|
, _cover(this, peer) {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
|
||||||
|
createBlocks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::createBlocks() {
|
||||||
|
auto user = _peer->asUser();
|
||||||
|
auto chat = _peer->asChat();
|
||||||
|
auto channel = _peer->asChannel();
|
||||||
|
auto megagroup = _peer->isMegagroup() ? channel : nullptr;
|
||||||
|
if (user || channel || megagroup) {
|
||||||
|
_blocks.push_back({ new InfoWidget(this, _peer), BlockSide::Right });
|
||||||
|
}
|
||||||
|
_blocks.push_back({ new SettingsWidget(this, _peer), BlockSide::Right });
|
||||||
|
if (chat || channel || megagroup) {
|
||||||
|
_blocks.push_back({ new InviteLinkWidget(this, _peer), BlockSide::Right });
|
||||||
|
}
|
||||||
|
_blocks.push_back({ new SharedMediaWidget(this, _peer), BlockSide::Right });
|
||||||
|
if (channel && !megagroup) {
|
||||||
|
_blocks.push_back({ new ChannelMembersWidget(this, _peer), BlockSide::Right });
|
||||||
|
}
|
||||||
|
_blocks.push_back({ new ActionsWidget(this, _peer), BlockSide::Right });
|
||||||
|
if (chat || megagroup) {
|
||||||
|
auto membersWidget = new MembersWidget(this, _peer);
|
||||||
|
connect(membersWidget, SIGNAL(onlineCountUpdated(int)), _cover, SLOT(onOnlineCountUpdated(int)));
|
||||||
|
_cover->onOnlineCountUpdated(membersWidget->onlineCount());
|
||||||
|
_blocks.push_back({ membersWidget, BlockSide::Left });
|
||||||
|
}
|
||||||
|
for_const (auto &blockData, _blocks) {
|
||||||
|
connect(blockData.block, SIGNAL(heightUpdated()), this, SLOT(onBlockHeightUpdated()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::resizeToWidth(int newWidth, int minHeight) {
|
||||||
|
int naturalHeight = resizeGetHeight(newWidth);
|
||||||
|
_addedHeight = qMax(minHeight - naturalHeight, 0);
|
||||||
|
resize(newWidth, naturalHeight + _addedHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
|
||||||
|
_visibleTop = visibleTop;
|
||||||
|
_visibleBottom = visibleBottom;
|
||||||
|
|
||||||
|
int notDisplayedAtBottom = height() - _visibleBottom;
|
||||||
|
if (notDisplayedAtBottom > 0) {
|
||||||
|
decreaseAdditionalHeight(notDisplayedAtBottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
for_const (auto blockData, _blocks) {
|
||||||
|
int blockY = blockData.block->y();
|
||||||
|
blockData.block->setVisibleTopBottom(visibleTop - blockY, visibleBottom - blockY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InnerWidget::shareContactButtonShown() const {
|
||||||
|
return _cover->shareContactButtonShown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::showFinished() {
|
||||||
|
_cover->showFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::decreaseAdditionalHeight(int removeHeight) {
|
||||||
|
resizeToWidth(width(), height() - removeHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
p.fillRect(e->rect(), st::profileBg);
|
||||||
|
|
||||||
|
if (_mode == Mode::TwoColumn) {
|
||||||
|
int leftHeight = countBlocksHeight(BlockSide::Left);
|
||||||
|
int rightHeight = countBlocksHeight(BlockSide::Right);
|
||||||
|
int shadowHeight = rightHeight;// qMin(leftHeight, rightHeight);
|
||||||
|
|
||||||
|
int shadowLeft = _blocksLeft + _leftColumnWidth + _columnDivider;
|
||||||
|
int shadowTop = _blocksTop + st::profileBlockMarginTop;
|
||||||
|
p.fillRect(rtlrect(shadowLeft, shadowTop, st::lineWidth, shadowHeight - st::profileBlockMarginTop, width()), st::shadowColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::keyPressEvent(QKeyEvent *e) {
|
||||||
|
if (e->key() == Qt::Key_Escape) {
|
||||||
|
emit cancelled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int InnerWidget::countBlocksHeight(BlockSide countSide) const {
|
||||||
|
int result = 0;
|
||||||
|
for_const (auto &blockData, _blocks) {
|
||||||
|
if (blockData.side != countSide || blockData.block->isHidden()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += blockData.block->height();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int InnerWidget::countBlocksLeft(int newWidth) const {
|
||||||
|
int result = st::profileBlockLeftMin;
|
||||||
|
result += (newWidth - st::wndMinWidth) / 2;
|
||||||
|
return qMin(result, st::profileBlockLeftMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
InnerWidget::Mode InnerWidget::countBlocksMode(int newWidth) const {
|
||||||
|
bool hasLeftWidget = false, hasRightWidget = false;
|
||||||
|
for_const (auto &blockData, _blocks) {
|
||||||
|
if (!blockData.block->isHidden()) {
|
||||||
|
if (blockData.side == BlockSide::Left) {
|
||||||
|
hasLeftWidget = true;
|
||||||
|
} else {
|
||||||
|
hasRightWidget = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasLeftWidget || !hasRightWidget) {
|
||||||
|
return Mode::OneColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int availWidth = newWidth - _blocksLeft;
|
||||||
|
if (availWidth >= st::profileBlockWideWidthMin + _columnDivider + st::profileBlockNarrowWidthMin) {
|
||||||
|
return Mode::TwoColumn;
|
||||||
|
}
|
||||||
|
return Mode::OneColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
int InnerWidget::countLeftColumnWidth(int newWidth) const {
|
||||||
|
int result = st::profileBlockWideWidthMin;
|
||||||
|
|
||||||
|
int availWidth = newWidth - _blocksLeft;
|
||||||
|
int additionalWidth = (availWidth - st::profileBlockWideWidthMin - _columnDivider - st::profileBlockNarrowWidthMin);
|
||||||
|
if (additionalWidth > 0) {
|
||||||
|
result += (additionalWidth / 2);
|
||||||
|
accumulate_min(result, st::profileBlockWideWidthMax);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::refreshBlocksPositions() {
|
||||||
|
auto layoutBlocks = [this](BlockSide layoutSide, int left) {
|
||||||
|
int top = _blocksTop;
|
||||||
|
for_const (auto &blockData, _blocks) {
|
||||||
|
if (_mode == Mode::TwoColumn && blockData.side != layoutSide) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (blockData.block->isHidden()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
blockData.block->moveToLeft(left, top);
|
||||||
|
top += blockData.block->height();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
layoutBlocks(BlockSide::Left, _blocksLeft);
|
||||||
|
if (_mode == Mode::TwoColumn) {
|
||||||
|
layoutBlocks(BlockSide::Right, _blocksLeft + _leftColumnWidth + _columnDivider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::resizeBlocks(int newWidth) {
|
||||||
|
for_const (auto &blockData, _blocks) {
|
||||||
|
int blockWidth = newWidth - _blocksLeft;
|
||||||
|
if (_mode == Mode::OneColumn) {
|
||||||
|
blockWidth -= _blocksLeft;
|
||||||
|
} else {
|
||||||
|
if (blockData.side == BlockSide::Left) {
|
||||||
|
blockWidth = _leftColumnWidth;
|
||||||
|
} else {
|
||||||
|
blockWidth -= _leftColumnWidth + _columnDivider;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockData.block->resizeToWidth(blockWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int InnerWidget::resizeGetHeight(int newWidth) {
|
||||||
|
_cover->resizeToWidth(newWidth);
|
||||||
|
|
||||||
|
_blocksTop = _cover->y() + _cover->height() + st::profileBlocksTop;
|
||||||
|
_blocksLeft = countBlocksLeft(newWidth);
|
||||||
|
_columnDivider = st::profileMemberPaddingLeft;
|
||||||
|
_mode = countBlocksMode(newWidth);
|
||||||
|
_leftColumnWidth = countLeftColumnWidth(newWidth);
|
||||||
|
resizeBlocks(newWidth);
|
||||||
|
|
||||||
|
refreshBlocksPositions();
|
||||||
|
|
||||||
|
update();
|
||||||
|
return countHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
int InnerWidget::countHeight() const {
|
||||||
|
int newHeight = _cover->height();
|
||||||
|
int leftHeight = countBlocksHeight(BlockSide::Left);
|
||||||
|
int rightHeight = countBlocksHeight(BlockSide::Right);
|
||||||
|
|
||||||
|
int blocksHeight = (_mode == Mode::OneColumn) ? (leftHeight + rightHeight) : qMax(leftHeight, rightHeight);
|
||||||
|
newHeight += st::profileBlocksTop + blocksHeight + st::profileBlocksBottom;
|
||||||
|
|
||||||
|
return newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InnerWidget::onBlockHeightUpdated() {
|
||||||
|
refreshBlocksPositions();
|
||||||
|
|
||||||
|
int naturalHeight = countHeight();
|
||||||
|
int notDisplayedAtBottom = naturalHeight - _visibleBottom;
|
||||||
|
if (notDisplayedAtBottom < 0) {
|
||||||
|
_addedHeight = -notDisplayedAtBottom;
|
||||||
|
} else {
|
||||||
|
_addedHeight = 0;
|
||||||
|
}
|
||||||
|
if (naturalHeight + _addedHeight != height()) {
|
||||||
|
resize(width(), naturalHeight + _addedHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|
113
Telegram/SourceFiles/profile/profile_inner_widget.h
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
class CoverWidget;
|
||||||
|
class BlockWidget;
|
||||||
|
|
||||||
|
class InnerWidget final : public TWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
InnerWidget(QWidget *parent, PeerData *peer);
|
||||||
|
|
||||||
|
PeerData *peer() const {
|
||||||
|
return _peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count new height for width=newWidth and resize to it.
|
||||||
|
void resizeToWidth(int newWidth, int minHeight);
|
||||||
|
|
||||||
|
// Updates the area that is visible inside the scroll container.
|
||||||
|
void setVisibleTopBottom(int visibleTop, int visibleBottom);
|
||||||
|
|
||||||
|
// Profile fixed top bar should use this flag to decide
|
||||||
|
// if it shows "Share contact" button or not.
|
||||||
|
// It should show it only if it is hidden in the cover.
|
||||||
|
bool shareContactButtonShown() const;
|
||||||
|
|
||||||
|
void showFinished();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void cancelled();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onBlockHeightUpdated();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createBlocks();
|
||||||
|
|
||||||
|
// Resizes content and counts natural widget height for the desired width.
|
||||||
|
int resizeGetHeight(int newWidth);
|
||||||
|
|
||||||
|
// Counts the natural widget height after resizing of child widgets.
|
||||||
|
int countHeight() const;
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
OneColumn,
|
||||||
|
TwoColumn,
|
||||||
|
};
|
||||||
|
enum class BlockSide {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
int countBlocksLeft(int newWidth) const;
|
||||||
|
Mode countBlocksMode(int newWidth) const;
|
||||||
|
int countLeftColumnWidth(int newWidth) const;
|
||||||
|
int countBlocksHeight(BlockSide countSide) const;
|
||||||
|
void resizeBlocks(int newWidth);
|
||||||
|
void refreshBlocksPositions();
|
||||||
|
|
||||||
|
// Sometimes height of this widget is larger than it is required
|
||||||
|
// so that it is allowed to scroll down to the desired position.
|
||||||
|
// When resizing with scroll moving up the additional height may be decreased.
|
||||||
|
void decreaseAdditionalHeight(int removeHeight);
|
||||||
|
|
||||||
|
PeerData *_peer;
|
||||||
|
|
||||||
|
// Height that we added to the natural height so that it is allowed
|
||||||
|
// to scroll down to the desired position.
|
||||||
|
int _addedHeight = 0;
|
||||||
|
int _visibleTop = 0;
|
||||||
|
int _visibleBottom = 0;
|
||||||
|
|
||||||
|
ChildWidget<CoverWidget> _cover;
|
||||||
|
|
||||||
|
int _blocksLeft = 0; // Caching countBlocksLeft() result.
|
||||||
|
int _blocksTop = 0;
|
||||||
|
int _columnDivider = 0;
|
||||||
|
int _leftColumnWidth = 0; // Caching countLeftColumnWidth() result.
|
||||||
|
struct Block {
|
||||||
|
BlockWidget *block;
|
||||||
|
BlockSide side;
|
||||||
|
};
|
||||||
|
QList<Block> _blocks;
|
||||||
|
|
||||||
|
Mode _mode = Mode::OneColumn;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Profile
|
113
Telegram/SourceFiles/profile/profile_invite_link_widget.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||||
|
|
||||||
|
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
It is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
In addition, as a special exception, the copyright holders give permission
|
||||||
|
to link the code of portions of this program with the OpenSSL library.
|
||||||
|
|
||||||
|
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||||
|
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||||
|
*/
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "profile/profile_invite_link_widget.h"
|
||||||
|
|
||||||
|
#include "styles/style_profile.h"
|
||||||
|
#include "ui/flatlabel.h"
|
||||||
|
#include "boxes/confirmbox.h"
|
||||||
|
#include "observer_peer.h"
|
||||||
|
#include "lang.h"
|
||||||
|
|
||||||
|
namespace Profile {
|
||||||
|
|
||||||
|
InviteLinkWidget::InviteLinkWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_invite_link_section)) {
|
||||||
|
auto observeEvents = Notify::PeerUpdate::Flag::InviteLinkChanged;
|
||||||
|
Notify::registerPeerObserver(observeEvents, this, &InviteLinkWidget::notifyPeerUpdated);
|
||||||
|
|
||||||
|
refreshLink();
|
||||||
|
refreshVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InviteLinkWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
|
||||||
|
if (update.peer != peer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.flags & Notify::PeerUpdate::Flag::InviteLinkChanged) {
|
||||||
|
refreshLink();
|
||||||
|
refreshVisibility();
|
||||||
|
|
||||||
|
contentSizeUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int InviteLinkWidget::resizeGetHeight(int newWidth) {
|
||||||
|
int newHeight = contentTop();
|
||||||
|
|
||||||
|
int marginLeft = st::profileBlockTextPart.margin.left();
|
||||||
|
int marginRight = st::profileBlockTextPart.margin.right();
|
||||||
|
int left = st::profileBlockTitlePosition.x();
|
||||||
|
if (_link) {
|
||||||
|
int textWidth = _link->naturalWidth();
|
||||||
|
int availableWidth = newWidth - left - st::profileBlockMarginRight;
|
||||||
|
int maxWidth = st::msgMaxWidth;
|
||||||
|
accumulate_min(textWidth, availableWidth);
|
||||||
|
accumulate_min(textWidth, st::msgMaxWidth);
|
||||||
|
_link->resizeToWidth(textWidth + marginLeft + marginRight);
|
||||||
|
_link->moveToLeft(left - marginLeft, newHeight - st::profileBlockTextPart.margin.top());
|
||||||
|
newHeight += _link->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
newHeight += st::profileBlockMarginBottom;
|
||||||
|
return newHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InviteLinkWidget::refreshVisibility() {
|
||||||
|
setVisible(_link != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InviteLinkWidget::getInviteLink() const {
|
||||||
|
if (auto chat = peer()->asChat()) {
|
||||||
|
return chat->inviteLink();
|
||||||
|
} else if (auto channel = peer()->asChannel()) {
|
||||||
|
return channel->inviteLink();
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
};
|
||||||
|
|
||||||
|
void InviteLinkWidget::refreshLink() {
|
||||||
|
_link.destroy();
|
||||||
|
TextWithEntities linkData = { getInviteLink(), EntitiesInText() };
|
||||||
|
if (!linkData.text.isEmpty()) {
|
||||||
|
_link = new FlatLabel(this, QString(), FlatLabel::InitType::Simple, st::profileInviteLinkText);
|
||||||
|
_link->show();
|
||||||
|
|
||||||
|
linkData.entities.push_back(EntityInText(EntityInTextUrl, 0, linkData.text.size()));
|
||||||
|
_link->setMarkedText(linkData);
|
||||||
|
_link->setSelectable(true);
|
||||||
|
_link->setContextCopyText(QString());
|
||||||
|
_link->setClickHandlerHook(func(this, &InviteLinkWidget::clickHandlerHook));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InviteLinkWidget::clickHandlerHook(const ClickHandlerPtr &handler, Qt::MouseButton button) {
|
||||||
|
auto link = getInviteLink();
|
||||||
|
if (link.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication::clipboard()->setText(link);
|
||||||
|
Ui::showLayer(new InformBox(lang(lng_group_invite_copied)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Profile
|