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
This commit is contained in:
John Preston 2016-06-06 18:16:52 +03:00
commit 520260f207
165 changed files with 10452 additions and 4055 deletions

View file

@ -20,13 +20,13 @@ downloadLibs() {
info_msg "QT-Version: ${_qtver}, SRC-Dir: ${srcdir}"
echo -e "Clone Qt\n"
git clone git://code.qt.io/qt/qt5.git qt5_6_0
cd qt5_6_0
git checkout 5.6
git clone git://code.qt.io/qt/qt5.git qt${_qtver}
cd qt${_qtver}
git checkout $(echo ${_qtver} | sed -e "s/\..$//")
perl init-repository --module-subset=qtbase,qtimageformats
git checkout v5.6.0
cd qtbase && git checkout v5.6.0 && cd ..
cd qtimageformats && git checkout v5.6.0 && cd ..
git checkout v${_qtver}
cd qtbase && git checkout v${_qtver} && cd ..
cd qtimageformats && git checkout v${_qtver} && cd ..
cd ..
echo -e "Clone Breakpad\n"
@ -48,9 +48,9 @@ prepare() {
mkdir -p "$srcdir/Libraries"
ln -s "$srcdir/qt5_6_0" "$srcdir/Libraries/qt5_6_0"
cd "$srcdir/Libraries/qt5_6_0/qtbase"
git apply "$srcdir/tdesktop/Telegram/Patches/qtbase_5_6_0.diff"
ln -s "$srcdir/qt${_qtver}" "$srcdir/Libraries/qt${_qtver}"
cd "$srcdir/Libraries/qt${_qtver}/qtbase"
git apply "$srcdir/tdesktop/Telegram/Patches/qtbase_$(echo ${_qtver} | sed -e "s/\./_/g").diff"
if [ ! -h "$srcdir/Libraries/breakpad" ]; then
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,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,/usr/local/tdesktop/Qt-5.6.0,$srcdir/qt,g" "$srcdir/tdesktop/Telegram/Telegram.pro"
local options=""
@ -98,7 +97,7 @@ build() {
info_msg "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 \
-qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb \
-qt-xkbcommon-x11 -no-opengl -static -nomake examples -nomake tests
@ -117,21 +116,21 @@ build() {
# Build codegen_style
mkdir -p "$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
info_msg "Build codegen_numbers"
# Build codegen_numbers
mkdir -p "$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
info_msg "Build MetaLang"
# Build MetaLang
mkdir -p "$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
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_numbers "-o./GeneratedFiles" "./../../Telegram/Resources/numbers.txt"
./../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
}

View file

@ -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';
# Set variables
_qtver=5.5.1
_qtver=5.6.0
srcdir=${PWD}
start_msg() {

View file

@ -11,7 +11,7 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
* Windows XP - Windows 10 (**not** RT)
* Mac OS X 10.8 - Mac OS X 10.11
* Mac OS X 10.6 - Mac OS X 10.7 (separate build)
* Ubuntu 12.04 - Ubuntu 15.04
* Ubuntu 12.04 - Ubuntu 16.04
* Fedora 22
## Third-party libraries

View file

@ -27,7 +27,4 @@ HEADERS += \
./SourceFiles/_other/memain.h \
./SourceFiles/_other/genemoji.h \
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\
./../../Libraries/QtStatic/qtbase/include/QtCore/5.5.1/QtCore\
./../../Libraries/QtStatic/qtbase/include\
include(qt_static.pri)

View file

@ -27,7 +27,4 @@ HEADERS += \
./SourceFiles/_other/mlmain.h \
./SourceFiles/_other/genlang.h \
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\
./../../Libraries/QtStatic/qtbase/include/QtCore/5.5.1/QtCore\
./../../Libraries/QtStatic/qtbase/include\
include(qt_static.pri)

View file

@ -35,8 +35,6 @@ unix {
}
}
INCLUDEPATH += ./../../Libraries/QtStatic/qtbase/include/QtGui/5.5.1/QtGui\
./../../Libraries/QtStatic/qtbase/include/QtCore/5.5.1/QtCore\
./../../Libraries/QtStatic/qtbase/include
include(qt_static.pri)
LIBS += -lcrypto -lssl -lz -llzma

View file

@ -24,3 +24,4 @@ using "basic_types.style";
using "basic.style";
using "overview/overview.style";
using "profile/profile.style";

View file

@ -55,10 +55,15 @@ wndMinWidth: 380px;
adaptiveNormalWidth: 640px;
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;
wndDefWidth: 800px;
wndDefHeight: 600px;
wndBG: white;
wndShadow: sprite(209px, 46px, 19px, 19px);
wndShadowShift: 1px;
@ -69,9 +74,10 @@ overBg: #edf2f5;
labelDefFlat: flatLabel {
font: font(fsize);
minWidth: 100px;
width: 0px;
maxHeight: 0px;
align: align(left);
textFg: windowTextFg;
}
boxBg: white;
@ -140,6 +146,29 @@ boxLabel: flatLabel(labelDefFlat) {
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 {
textFg: black;
textMargins: margins(5px, 6px, 5px, 4px);
@ -370,9 +399,10 @@ statusFont: font(fsize);
versionColor: #777;
shadowColor: rgba(0, 0, 0, 24);
shadowToggleDuration: 200;
slideDuration: 240;
slideShift: 0.3;
slideShift: 100px;
slideFadeOut: 0.3;
slideShadow: sprite(348px, 71px, 48px, 1px);
slideFunction: transition(easeOutCirc);
@ -1657,18 +1687,18 @@ confirmCompressedSkip: 10px;
profileMaxWidth: 410px;
profilePadding: margins(28px, 30px, 28px, 0px);
profilePhotoSize: 120px;
profileNameLeft: 21px;
profileNameTop: -1px;
profileNameFont: font(20px);
profileStatusLeft: 22px;
profileStatusTop: 31px;
profileStatusFont: font(fsize);
//profilePhotoSize: 120px;
//profileNameLeft: 21px;
//profileNameTop: -1px;
//profileNameFont: font(20px);
//profileStatusLeft: 22px;
//profileStatusTop: 31px;
//profileStatusFont: font(fsize);
profilePhoneLeft: 22px;
profilePhoneTop: 62px;
profilePhoneFont: font(16px);
profileButtonTop: 18px;
profileButtonSkip: 10px;
//profileButtonTop: 18px;
//profileButtonSkip: 10px;
profileHeaderFont: font(20px);
profileHeaderColor: black;
profileHeaderSkip: 59px;
@ -2444,7 +2474,7 @@ inlineResultsSkip: 3px;
inlineMediaHeight: 96px;
inlineThumbSize: 64px;
inlineThumbSkip: 10px;
inlineDescriptionFg: #8a8a8a;
inlineDescriptionFg: windowSubTextFg;
inlineRowMargin: 6px;
inlineRowBorder: 1px;
inlineRowBorderFg: #eaeaea;

View file

@ -210,9 +210,11 @@ slider {
flatLabel {
font: font;
minWidth: pixels;
margin: margins;
width: pixels;
align: align;
textFg: color;
maxHeight: pixels;
}
switcher {
@ -306,6 +308,8 @@ BoxButton {
textTop: pixels;
icon: icon;
font: font;
duration: int;
}
@ -411,3 +415,18 @@ PeerAvatarButton {
size: pixels;
photoSize: pixels;
}
OutlineButton {
outlineWidth: pixels;
outlineFg: color;
outlineFgOver: color;
textBg: color;
textBgOver: color;
textFg: color;
textFgOver: color;
font: font;
padding: margins;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

View file

@ -117,8 +117,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_channel_status" = "channel";
"lng_group_status" = "group";
"lng_channel_members_link" = "{count:_not_used_|# member|# members} »";
"lng_channel_admins_link" = "{count:Manage administrators|# administrator|# administrators} »";
"lng_channel_members_link" = "{count:_not_used_|# member|# members}";
"lng_channel_admins_link" = "{count:_not_used_|# administrator|# administrators}";
"lng_server_error" = "Internal server error.";
"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_bot_settings" = "Settings";
"lng_profile_bot_help" = "Help";
"lng_profile_invite_link_section" = "Invite link";
"lng_profile_create_public_link" = "Create public link";
"lng_profile_edit_public_link" = "Edit public link";
"lng_profile_manage_admins" = "Manage administrators";
"lng_profile_participants_section" = "Members";
"lng_profile_info" = "Contact info";
"lng_profile_group_info" = "Group info";
"lng_profile_channel_info" = "Channel info";
"lng_profile_info_section" = "Info";
"lng_profile_mobile_number" = "Mobile:";
"lng_profile_username" = "Username:";
"lng_profile_link" = "Link:";
"lng_profile_add_contact" = "Add Contact";
"lng_profile_edit_contact" = "Edit";
"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_leave_group" = "Leave group";
"lng_profile_delete_group" = "Delete group";
"lng_profile_report" = "Report";
"lng_profile_search_messages" = "Search for messages";
"lng_profile_block_user" = "Block 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_set_group_photo" = "Set Photo";
"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_kick" = "Remove";
"lng_profile_admin" = "admin";
@ -441,20 +447,33 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_profile_loading" = "Loading...";
"lng_profile_shared_media" = "Shared media";
"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_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_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_files" = "{count:_not_used_|# file|# files} »";
"lng_profile_files" = "{count:_not_used_|# file|# files}";
"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_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_copy_phone" = "Copy phone number";
"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_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_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_feature1" = "— The members limit is {count:_not_used_|# user|# users}";
"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_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_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_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
"lng_topbar_info" = "Info";
"lng_profile_group_info" = "Group info";
"lng_profile_channel_info" = "Channel info";
// Wnd specific

View file

@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,49,0
PRODUCTVERSION 0,9,49,0
FILEVERSION 0,9,49,1
PRODUCTVERSION 0,9,49,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.9.49.0"
VALUE "FileVersion", "0.9.49.1"
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.49.0"
VALUE "ProductVersion", "0.9.49.1"
END
END
BLOCK "VarFileInfo"

View file

@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,49,0
PRODUCTVERSION 0,9,49,0
FILEVERSION 0,9,49,1
PRODUCTVERSION 0,9,49,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -43,10 +43,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Updater"
VALUE "FileVersion", "0.9.49.0"
VALUE "FileVersion", "0.9.49.1"
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.49.0"
VALUE "ProductVersion", "0.9.49.1"
END
END
BLOCK "VarFileInfo"

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "apiwrap.h"
#include "observer_peer.h"
#include "lang.h"
#include "application.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) {
switch (msgs.type()) {
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);
}
App::feedUsers(d.vusers, false);
App::feedChats(d.vchats, false);
App::feedUsers(d.vusers);
App::feedChats(d.vchats);
if (peer->isChat()) {
if (d.vfull_chat.type() != mtpc_chatFull) {
@ -206,9 +211,9 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
} else {
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()) {
if (d.vfull_chat.type() != mtpc_channelFull) {
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());
PhotoData *photo = App::feedPhoto(f.vchat_photo);
ChannelData *channel = peer->asChannel();
auto canViewAdmins = channel->canViewAdmins();
auto canViewMembers = channel->canViewMembers();
channel->flagsFull = f.vflags.v;
if (photo) {
channel->photoId = photo->id;
@ -267,17 +276,10 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
} break;
}
}
channel->about = qs(f.vabout);
int32 newCount = f.has_participants_count() ? f.vparticipants_count.v : 0;
if (newCount != channel->count) {
if (channel->isMegagroup() && !channel->mgInfo->lastParticipants.isEmpty()) {
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();
channel->setAbout(qs(f.vabout));
channel->setMembersCount(f.has_participants_count() ? f.vparticipants_count.v : 0);
channel->setAdminsCount(f.has_admins_count() ? f.vadmins_count.v : 0);
channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
if (History *h = App::historyLoaded(channel->id)) {
if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) {
h->setUnreadCount(f.vunread_count.v);
@ -294,7 +296,10 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
}
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) {
@ -313,18 +318,17 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
}
App::clearPeerUpdated(peer);
emit fullPeerUpdated(peer);
App::emitPeerUpdated();
}
void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req) {
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()) {
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()) {
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
notifySettingReceived(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
}
if (d.has_bot_info()) {
@ -332,8 +336,8 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI
} else {
peer->asUser()->setBotInfoVersion(-1);
}
peer->asUser()->blocked = d.is_blocked() ? UserIsBlocked : UserIsNotBlocked;
peer->asUser()->about = d.has_about() ? qs(d.vabout) : QString();
peer->asUser()->setBlockStatus(d.is_blocked() ? UserData::BlockStatus::Blocked : UserData::BlockStatus::NotBlocked);
peer->asUser()->setAbout(d.has_about() ? qs(d.vabout) : QString());
if (req) {
QMap<PeerData*, mtpRequestId>::iterator i = _fullPeerRequests.find(peer);
@ -343,7 +347,6 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI
}
App::clearPeerUpdated(peer);
emit fullPeerUpdated(peer);
App::emitPeerUpdated();
}
bool ApiWrap::gotPeerFullFailed(PeerData *peer, const RPCError &error) {
@ -538,14 +541,19 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP
h->clearLastKeyboard();
if (App::main()) App::main()->updateBotKeyboard(h);
}
if (d.vcount.v > peer->count) {
peer->count = d.vcount.v;
} else if (v.count() > peer->count) {
peer->count = v.count();
int newMembersCount = qMax(d.vcount.v, v.count());
if (newMembersCount > peer->membersCount()) {
peer->setMembersCount(newMembersCount);
}
if (!bots && v.isEmpty()) {
peer->count = peer->mgInfo->lastParticipants.size();
if (!bots) {
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;
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) {
_kickRequests.remove(kick);
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) {
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 {
kick.first->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
kick.first->asChannel()->mgInfo->lastParticipantsCount = 0;
megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
megagroupInfo->lastParticipantsCount = 0;
}
if (kick.first->asChannel()->mgInfo->lastAdmins.contains(kick.second)) {
kick.first->asChannel()->mgInfo->lastAdmins.remove(kick.second);
if (kick.first->asChannel()->adminsCount > 1) {
--kick.first->asChannel()->adminsCount;
if (megagroupInfo->lastAdmins.contains(kick.second)) {
megagroupInfo->lastAdmins.remove(kick.second);
if (channel->adminsCount() > 1) {
channel->setAdminsCount(channel->adminsCount() - 1);
}
Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::AdminsChanged);
}
kick.first->asChannel()->mgInfo->bots.remove(kick.second);
if (kick.first->asChannel()->mgInfo->bots.isEmpty() && kick.first->asChannel()->mgInfo->botStatus > 0) {
kick.first->asChannel()->mgInfo->botStatus = -1;
megagroupInfo->bots.remove(kick.second);
if (megagroupInfo->bots.isEmpty() && megagroupInfo->botStatus > 0) {
megagroupInfo->botStatus = -1;
}
}
Notify::peerUpdatedDelayed(kick.first, Notify::PeerUpdate::Flag::MembersChanged);
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) {
_stickerSetRequests.remove(setId);

View file

@ -28,7 +28,7 @@ public:
ApiWrap(QObject *parent);
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 requestFullPeer(PeerData *peer);
@ -50,6 +50,15 @@ public:
void scheduleStickerSetRequest(uint64 setId, uint64 access);
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();
signals:
@ -65,6 +74,8 @@ public slots:
private:
void updatesReceived(const MTPUpdates &updates);
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
struct MessageDataRequest {
MessageDataRequest() : req(0) {
@ -120,4 +131,24 @@ private:
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
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);
};

View file

@ -35,6 +35,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h"
#include "apiwrap.h"
#include "numbers.h"
#include "observer_peer.h"
namespace {
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) {
return -1;
}
int32 online = user->onlineTill;
TimeId online = user->onlineTill;
if (online <= 0) {
switch (online) {
case 0:
@ -252,11 +253,14 @@ namespace {
return online;
}
int32 onlineWillChangeIn(UserData *user, int32 now) {
int32 onlineWillChangeIn(UserData *user, TimeId now) {
if (isServiceUser(user->id) || user->botInfo) {
return 86400;
}
int32 online = user->onlineTill;
return onlineWillChangeIn(user->onlineTill, now);
}
int32 onlineWillChangeIn(TimeId online, TimeId now) {
if (online <= 0) {
if (-online > now) return -online - now;
return 86400;
@ -276,7 +280,7 @@ namespace {
return dNow.secsTo(dTomorrow);
}
QString onlineText(UserData *user, int32 now, bool precise) {
QString onlineText(UserData *user, TimeId now, bool precise) {
if (isNotificationsUser(user->id)) {
return lang(lng_status_service_notifications);
} else if (isServiceUser(user->id)) {
@ -284,7 +288,10 @@ namespace {
} else if (user->botInfo) {
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) {
switch (online) {
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) {
return false;
}
int32 online = user->onlineTill;
return onlineColorUse(user->onlineTill, now);
}
bool onlineColorUse(TimeId online, TimeId now) {
if (online <= 0) {
switch (online) {
case 0:
@ -364,21 +374,25 @@ namespace {
return (online > now);
}
UserData *feedUsers(const MTPVector<MTPUser> &users, bool emitPeerUpdated) {
UserData *data = 0;
const auto &v(users.c_vector().v);
for (QVector<MTPUser>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
const auto &user(*i);
data = 0;
UserData *feedUsers(const MTPVector<MTPUser> &users) {
UserData *result = nullptr;
for_const (auto &user, users.c_vector().v) {
UserData *data = nullptr;
bool wasContact = false, minimal = false;
const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
Notify::PeerUpdate update;
using UpdateFlag = Notify::PeerUpdate::Flag;
switch (user.type()) {
case mtpc_userEmpty: {
const auto &d(user.c_userEmpty());
PeerId peer(peerFromUser(d.vid.v));
data = App::user(peer);
auto canShareThisContact = data->canShareThisContactFast();
wasContact = data->isContact();
data->input = MTP_inputPeerUser(d.vid, MTP_long(0));
data->inputUser = MTP_inputUser(d.vid, MTP_long(0));
data->setName(lang(lng_deleted), QString(), QString(), QString());
@ -386,9 +400,11 @@ namespace {
data->access = UserNoAccess;
data->flags = 0;
data->setBotInfoVersion(-1);
wasContact = (data->contact > 0);
status = &emptyStatus;
data->contact = -1;
if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact;
if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact;
} break;
case mtpc_user: {
const auto &d(user.c_user());
@ -396,6 +412,8 @@ namespace {
PeerId peer(peerFromUser(d.vid.v));
data = App::user(peer);
auto canShareThisContact = data->canShareThisContactFast();
wasContact = data->isContact();
if (!minimal) {
data->flags = d.vflags.v;
if (d.is_self()) {
@ -415,7 +433,10 @@ namespace {
}
}
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->setPhoto(MTP_userProfilePhotoEmpty());
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 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());
bool phoneChanged = (data->phone != phone);
if (phoneChanged) data->setPhone(phone);
bool phoneChanged = (data->phone() != phone);
if (phoneChanged) {
data->setPhone(phone);
update.flags |= UpdateFlag::UserPhoneChanged;
}
bool nameChanged = (data->firstName != fname) || (data->lastName != lname);
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;
status = d.has_status() ? &d.vstatus : &emptyStatus;
}
wasContact = (data->contact > 0);
if (!minimal) {
if (d.has_bot_info_version()) {
data->setBotInfoVersion(d.vbot_info_version.v);
@ -468,7 +490,7 @@ namespace {
} else {
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) {
cRefReportSpamStatuses().insert(data->id, dbiprsHidden);
Local::writeReportSpamStatuses();
@ -478,6 +500,9 @@ namespace {
if (App::wnd()) App::wnd()->updateGlobalMenu();
}
}
if (canShareThisContact != data->canShareThisContactFast()) update.flags |= UpdateFlag::UserCanShareContact;
if (wasContact != data->isContact()) update.flags |= UpdateFlag::UserIsContact;
} break;
}
@ -490,6 +515,8 @@ namespace {
} else if (data->loadedStatus != PeerData::FullLoaded) {
data->loadedStatus = PeerData::FullLoaded;
}
auto oldOnlineTill = data->onlineTill;
if (status && !minimal) switch (status->type()) {
case mtpc_userStatusEmpty: data->onlineTill = 0; break;
case mtpc_userStatusRecently:
@ -502,8 +529,11 @@ namespace {
case mtpc_userStatusOffline: data->onlineTill = status->c_userStatusOffline().vwas_online.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;
}
if (App::main()) {
@ -511,34 +541,42 @@ namespace {
Notify::userIsContactChanged(data);
}
if (emitPeerUpdated) {
App::main()->peerUpdated(data);
} else {
markPeerUpdated(data);
markPeerUpdated(data);
if (update.flags) {
update.peer = data;
Notify::peerUpdatedDelayed(update);
}
}
result = data;
}
return data;
return result;
}
PeerData *feedChats(const MTPVector<MTPChat> &chats, bool emitPeerUpdated) {
PeerData *data = 0;
const auto &v(chats.c_vector().v);
for (QVector<MTPChat>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
const auto &chat(*i);
data = 0;
PeerData *feedChats(const MTPVector<MTPChat> &chats) {
PeerData *result = nullptr;
for_const (auto &chat, chats.c_vector().v) {
PeerData *data = nullptr;
bool minimal = false;
Notify::PeerUpdate update;
using UpdateFlag = Notify::PeerUpdate::Flag;
switch (chat.type()) {
case mtpc_chat: {
const auto &d(chat.c_chat());
auto &d(chat.c_chat());
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->updateName(qs(d.vtitle), QString(), QString());
ChatData *cdata = data->asChat();
cdata->setName(qs(d.vtitle));
cdata->setPhoto(d.vphoto);
cdata->date = d.vdate.v;
@ -571,9 +609,11 @@ namespace {
}
}
Notify::migrateUpdated(channel);
update.flags |= UpdateFlag::MigrationChanged;
}
if (updatedTo) {
Notify::migrateUpdated(cdata);
update.flags |= UpdateFlag::MigrationChanged;
}
}
@ -584,43 +624,52 @@ namespace {
cdata->count = d.vparticipants_count.v;
cdata->isForbidden = false;
if (cdata->version < d.vversion.v) {
cdata->version = d.vversion.v;
cdata->invalidateParticipants();
if (canEdit != cdata->canEdit()) {
update.flags |= UpdateFlag::ChatCanEdit;
}
} break;
case mtpc_chatForbidden: {
const auto &d(chat.c_chatForbidden());
auto &d(chat.c_chatForbidden());
data = App::chat(peerFromChat(d.vid.v));
auto cdata = data->asChat();
auto canEdit = cdata->canEdit();
data->input = MTP_inputPeerChat(d.vid);
data->updateName(qs(d.vtitle), QString(), QString());
ChatData *cdata = data->asChat();
cdata->setName(qs(d.vtitle));
cdata->setPhoto(MTP_chatPhotoEmpty());
cdata->date = 0;
cdata->count = -1;
cdata->invalidateParticipants();
cdata->flags = 0;
cdata->isForbidden = true;
if (canEdit != cdata->canEdit()) {
update.flags |= UpdateFlag::ChatCanEdit;
}
} break;
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();
if (minimal) {
data = App::channelLoaded(peer);
data = App::channelLoaded(peerId);
if (!data) {
continue; // minimal is not loaded, need to make getDifference
}
} 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));
}
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) {
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);
@ -644,15 +693,32 @@ namespace {
cdata->isForbidden = false;
cdata->flagsUpdated();
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;
case mtpc_channelForbidden: {
const auto &d(chat.c_channelForbidden());
auto &d(chat.c_channelForbidden());
PeerId peer(peerFromChannel(d.vid.v));
data = App::channel(peer);
auto peerId = peerFromChannel(d.vid.v);
data = App::channel(peerId);
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);
auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
@ -663,9 +729,19 @@ namespace {
cdata->access = d.vaccess_hash.v;
cdata->setPhoto(MTP_chatPhotoEmpty());
cdata->date = 0;
cdata->count = 0;
cdata->setMembersCount(0);
cdata->isForbidden = true;
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;
}
if (!data) continue;
@ -678,14 +754,15 @@ namespace {
data->loadedStatus = PeerData::FullLoaded;
}
if (App::main()) {
if (emitPeerUpdated) {
App::main()->peerUpdated(data);
} else {
markPeerUpdated(data);
markPeerUpdated(data);
if (update.flags) {
update.peer = data;
Notify::peerUpdatedDelayed(update);
}
}
result = data;
}
return data;
return result;
}
void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated) {
@ -701,6 +778,7 @@ namespace {
case mtpc_chatParticipants: {
const auto &d(p.c_chatParticipants());
chat = App::chat(d.vchat_id.v);
auto canEdit = chat->canEdit();
if (!requestBotInfos || chat->version <= d.vversion.v) { // !requestBotInfos is true on getFullChat result
chat->version = d.vversion.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;
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged | Notify::PeerUpdate::Flag::AdminsChanged);
if (chat && App::main()) {
if (emitPeerUpdated) {
App::main()->peerUpdated(chat);
@ -820,6 +902,7 @@ namespace {
chat->invalidateParticipants();
chat->count++;
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged);
if (App::main()) {
if (emitPeerUpdated) {
App::main()->peerUpdated(chat);
@ -845,6 +928,7 @@ namespace {
}
} else if (chat->version <= d.vversion.v && chat->count > 0) {
chat->version = d.vversion.v;
auto canEdit = chat->canEdit();
UserData *user = App::userLoaded(d.vuser_id.v);
if (user) {
if (chat->participants.isEmpty()) {
@ -886,6 +970,10 @@ namespace {
chat->invalidateParticipants();
chat->count--;
}
if (canEdit != chat->canEdit()) {
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::ChatCanEdit);
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::MembersChanged);
if (App::main()) {
if (emitPeerUpdated) {
App::main()->peerUpdated(chat);
@ -906,13 +994,14 @@ namespace {
}
chat->version = d.vversion.v;
if (mtpIsTrue(d.venabled)) {
chat->flags |= MTPDchat::Flag::f_admins_enabled;
if (!badVersion) {
chat->invalidateParticipants();
}
chat->flags |= MTPDchat::Flag::f_admins_enabled;
} else {
chat->flags &= ~MTPDchat::Flag::f_admins_enabled;
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
if (emitPeerUpdated) {
App::main()->peerUpdated(chat);
} else {
@ -936,6 +1025,7 @@ namespace {
}
} else if (chat->version <= d.vversion.v && chat->count > 0) {
chat->version = d.vversion.v;
auto canEdit = chat->canEdit();
UserData *user = App::userLoaded(d.vuser_id.v);
if (user) {
if (mtpIsTrue(d.vis_admin)) {
@ -956,6 +1046,10 @@ namespace {
} else {
chat->invalidateParticipants();
}
if (canEdit != chat->canEdit()) {
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::ChatCanEdit);
}
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
if (App::main()) {
if (emitPeerUpdated) {
App::main()->peerUpdated(chat);
@ -1163,33 +1257,10 @@ namespace {
}
}
void feedUserLinks(const MTPVector<MTPcontacts_Link> &links, bool emitPeerUpdated) {
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) {
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink) {
UserData *user = userLoaded(userId.v);
if (user) {
bool wasContact = (user->contact > 0);
auto wasContact = user->isContact();
bool wasShowPhone = !user->contact;
switch (myLink.type()) {
case mtpc_contactLinkContact:
@ -1208,10 +1279,14 @@ namespace {
break;
}
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;
}
}
if (wasContact != user->isContact()) {
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserIsContact);
}
if ((user->contact > 0 && !wasContact) || (wasContact && user->contact < 1)) {
Notify::userIsContactChanged(user);
}
@ -1219,15 +1294,9 @@ namespace {
bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
if (showPhoneChanged) {
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);
}
user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), textOneLine(user->username));
}
markPeerUpdated(user);
}
}
@ -2272,6 +2341,10 @@ namespace {
return result;
}
QPixmap pixmapFromImageInPlace(QImage &&image) {
return QPixmap::fromImage(std_::forward<QImage>(image), Qt::ColorOnly);
}
void regPhotoItem(PhotoData *data, HistoryItem *item) {
::photoItems[data].insert(item, NullType());
}
@ -2317,11 +2390,21 @@ namespace {
}
void regSharedContactItem(int32 userId, HistoryItem *item) {
auto user = App::userLoaded(userId);
auto canShareThisContact = user ? user->canShareThisContact() : false;
::sharedContactItems[userId].insert(item, NullType());
if (canShareThisContact != (user ? user->canShareThisContact() : false)) {
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserCanShareContact);
}
}
void unregSharedContactItem(int32 userId, HistoryItem *item) {
auto user = App::userLoaded(userId);
auto canShareThisContact = user ? user->canShareThisContact() : false;
::sharedContactItems[userId].remove(item);
if (canShareThisContact != (user ? user->canShareThisContact() : false)) {
Notify::peerUpdatedDelayed(user, Notify::PeerUpdate::Flag::UserCanShareContact);
}
}
const SharedContactItems &sharedContactItems() {

View file

@ -59,13 +59,17 @@ namespace App {
QString formatPhone(QString phone);
int32 onlineForSort(UserData *user, int32 now);
int32 onlineWillChangeIn(UserData *user, int32 nowOnServer);
QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
bool onlineColorUse(UserData *user, int32 now);
TimeId onlineForSort(UserData *user, TimeId now);
int32 onlineWillChangeIn(UserData *user, TimeId now);
int32 onlineWillChangeIn(TimeId online, TimeId 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 feedParticipantAdd(const MTPDupdateChatParticipantAdd &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 feedOutboxRead(const PeerId &peer, MsgId upTo);
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, bool emitPeerUpdated = true);
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
void markPeerUpdated(PeerData *data);
void clearPeerUpdated(PeerData *data);
void emitPeerUpdated();
ImagePtr image(const MTPPhotoSize &size);
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(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 unregPhotoItem(PhotoData *data, HistoryItem *item);

View file

@ -27,9 +27,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "lang.h"
#include "boxes/confirmbox.h"
#include "ui/filedialog.h"
#include "langloaderplain.h"
#include "localstorage.h"
#include "autoupdater.h"
#include "core/observer.h"
#include "observer_peer.h"
namespace {
void mtpStateChanged(int32 dc, int32 state) {
@ -200,6 +203,7 @@ void Application::singleInstanceChecked() {
Logs::multipleInstances();
}
Notify::startObservers();
Sandbox::start();
if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
@ -336,6 +340,8 @@ void Application::closeApplication() {
if (_updateThread) _updateThread->quit();
_updateThread = 0;
#endif
Notify::finishObservers();
}
#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() {
uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {

View file

@ -203,6 +203,8 @@ public slots:
void call_handleHistoryUpdate();
void call_handleUnreadCounterUpdate();
void call_handleFileDialogQueue();
void call_handleDelayedPeerUpdates();
private:

View file

@ -32,9 +32,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
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)
, _text1(this, lang(lng_about_text_1), st::aboutLabel, st::aboutTextStyle)
, _text2(this, lang(lng_about_text_2), st::aboutLabel, st::aboutTextStyle)
, _text3(this, QString(), 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), FlatLabel::InitType::Rich, st::aboutLabel, st::aboutTextStyle)
, _text3(this,st::aboutLabel, st::aboutTextStyle)
, _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]")));
@ -109,10 +109,9 @@ void AboutBox::paintEvent(QPaintEvent *e) {
#ifndef TDESKTOP_DISABLE_CRASH_REPORTS
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());
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
auto file = psConvertFileUrl(m->urls().at(0));
return file.endsWith(qstr(".telegramcrash"), Qt::CaseInsensitive) ? file : QString();
}

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "abstractbox.h"
#include "ui/flatlabel.h"
class AboutBox : public AbstractBox {
Q_OBJECT

View file

@ -30,18 +30,16 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "mainwindow.h"
#include "apiwrap.h"
#include "observer_peer.h"
AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth)
, _user(0)
, _save(this, lang(lng_add_contact), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton)
, _first(this, st::defaultInputField, lang(lng_signup_firstname), fname)
, _last(this, st::defaultInputField, lang(lng_signup_lastname), lname)
, _phone(this, st::defaultInputField, lang(lng_contact_phone), phone)
, _invertOrder(langFirstNameGoesSecond())
, _contactId(0)
, _addRequest(0) {
, _invertOrder(langFirstNameGoesSecond()) {
if (!phone.isEmpty()) {
_phone.setDisabled(true);
}
@ -56,10 +54,8 @@ AddContactBox::AddContactBox(UserData *user) : AbstractBox(st::boxWidth)
, _retry(this, lang(lng_try_other_contact), st::defaultBoxButton)
, _first(this, st::defaultInputField, lang(lng_signup_firstname), user->firstName)
, _last(this, st::defaultInputField, lang(lng_signup_lastname), user->lastName)
, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone)
, _invertOrder(langFirstNameGoesSecond())
, _contactId(0)
, _addRequest(0) {
, _phone(this, st::defaultInputField, lang(lng_contact_phone), user->phone())
, _invertOrder(langFirstNameGoesSecond()) {
_phone.setDisabled(true);
initBox();
}
@ -190,7 +186,7 @@ void AddContactBox::onSave() {
_sentName = firstName;
if (_user) {
_contactId = rand_value<uint64>();
QVector<MTPInputContact> v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_user->phone), MTP_string(firstName), MTP_string(lastName)));
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));
} else {
_contactId = rand_value<uint64>();
@ -206,7 +202,7 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
QString err(error.type());
QString firstName = _first.getLastText().trimmed(), lastName = _last.getLastText().trimmed();
if (err == "CHAT_TITLE_NOT_MODIFIED") {
_user->updateName(firstName, QString(), QString());
_user->setName(firstName, lastName, _user->nameOrPhone, _user->username);
emit closed();
return true;
} else if (err == "NO_CHAT_TITLE") {
@ -548,7 +544,7 @@ bool GroupInfoBox::creationFail(const RPCError &error) {
void GroupInfoBox::exportDone(const MTPExportedChatInvite &result) {
_creationRequestId = 0;
if (result.type() == mtpc_chatInviteExported) {
_createdChannel->invitationUrl = qs(result.c_chatInviteExported().vlink);
_createdChannel->setInviteLink(qs(result.c_chatInviteExported().vlink));
}
Ui::showLayer(new SetupChannelBox(_createdChannel));
}
@ -712,7 +708,7 @@ void SetupChannelBox::paintEvent(QPaintEvent *e) {
option.setWrapMode(QTextOption::WrapAnywhere);
p.setFont(_linkOver ? st::boxTextFont->underline() : st::boxTextFont);
p.setPen(st::btnDefLink.color);
p.drawText(_invitationLink, _channel->invitationUrl, option);
p.drawText(_invitationLink, _channel->inviteLink(), option);
if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) {
p.setOpacity(a_goodOpacity.current());
p.setPen(st::setGoodColor);
@ -753,7 +749,7 @@ void SetupChannelBox::mouseMoveEvent(QMouseEvent *e) {
void SetupChannelBox::mousePressEvent(QMouseEvent *e) {
mouseMoveEvent(e);
if (_linkOver) {
Application::clipboard()->setText(_channel->invitationUrl);
Application::clipboard()->setText(_channel->inviteLink());
_goodTextLink = lang(lng_create_channel_link_copied);
a_goodOpacity = anim::fvalue(1, 0);
_a_goodFade.start();
@ -1140,7 +1136,9 @@ bool EditNameTitleBox::onSaveChatFail(const RPCError &error) {
_requestId = 0;
QString err(error.type());
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();
return true;
} else if (err == qstr("NO_CHAT_TITLE")) {
@ -1162,7 +1160,7 @@ EditChannelBox::EditChannelBox(ChannelData *channel) : AbstractBox()
, _save(this, lang(lng_settings_save), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, _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())
, _publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link), st::defaultBoxLinkButton)
, _saveTitleRequestId(0)
@ -1303,7 +1301,7 @@ void EditChannelBox::onPublicLink() {
}
void EditChannelBox::saveDescription() {
if (_sentDescription == _channel->about) {
if (_sentDescription == _channel->about()) {
saveSign();
} else {
_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) {
_saveDescriptionRequestId = 0;
if (err == qstr("CHAT_ABOUT_NOT_MODIFIED")) {
_channel->about = _sentDescription;
if (App::api()) {
emit App::api()->fullPeerUpdated(_channel);
if (_channel->setAbout(_sentDescription)) {
if (App::api()) {
emit App::api()->fullPeerUpdated(_channel);
}
}
saveSign();
return true;
@ -1367,9 +1366,10 @@ void EditChannelBox::onSaveTitleDone(const MTPUpdates &updates) {
void EditChannelBox::onSaveDescriptionDone(const MTPBool &result) {
_saveDescriptionRequestId = 0;
_channel->about = _sentDescription;
if (App::api()) {
emit App::api()->fullPeerUpdated(_channel);
if (_channel->setAbout(_sentDescription)) {
if (App::api()) {
emit App::api()->fullPeerUpdated(_channel);
}
}
saveSign();
}

View file

@ -57,7 +57,7 @@ private:
void initBox();
UserData *_user;
UserData *_user = nullptr;
QString _boxTitle;
BoxButton _save, _cancel, _retry;
@ -66,9 +66,9 @@ private:
bool _invertOrder;
uint64 _contactId;
uint64 _contactId = 0;
mtpRequestId _addRequest;
mtpRequestId _addRequest = 0;
QString _sentName;
};

View file

@ -381,11 +381,10 @@ void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) {
PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth)
, _channel(channel)
, _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)
, _pin(this, lang(lng_pinned_pin), st::defaultBoxButton)
, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
, _requestId(0) {
, _cancel(this, lang(lng_cancel), st::cancelBoxButton) {
_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());
@ -441,7 +440,7 @@ RichDeleteMessageBox::RichDeleteMessageBox(ChannelData *channel, UserData *from,
, _channel(channel)
, _from(from)
, _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)
, _reportSpam(this, lang(lng_report_spam), 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)) {
bool wasLast = (item->history()->lastMsg == item);
item->destroy();
if (_msgId > 0) {
App::main()->deleteMessages(_channel, QVector<MTPint>(1, MTP_int(_msgId)));
} else if (wasLast) {

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "abstractbox.h"
#include "ui/flatlabel.h"
class InformBox;
class ConfirmBox : public AbstractBox, public ClickHandlerHost {
@ -216,7 +217,7 @@ private:
BoxButton _pin, _cancel;
mtpRequestId _requestId;
mtpRequestId _requestId = 0;
};

View file

@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "ui/filedialog.h"
#include "boxes/photocropbox.h"
#include "boxes/confirmbox.h"
#include "observer_peer.h"
#include "apiwrap.h"
QString cantInviteError() {
@ -201,16 +202,20 @@ void ContactsInner::addAdminDone(const MTPUpdates &result, mtpRequestId req) {
_addAdminRequestId = 0;
if (_addAdmin && _channel && _channel->isMegagroup()) {
Notify::PeerUpdate update(_channel);
if (_channel->mgInfo->lastParticipants.indexOf(_addAdmin) < 0) {
_channel->mgInfo->lastParticipants.push_front(_addAdmin);
update.flags |= Notify::PeerUpdate::Flag::MembersChanged;
}
_channel->mgInfo->lastAdmins.insert(_addAdmin);
update.flags |= Notify::PeerUpdate::Flag::AdminsChanged;
if (_addAdmin->botInfo) {
_channel->mgInfo->bots.insert(_addAdmin);
if (_channel->mgInfo->botStatus != 0 && _channel->mgInfo->botStatus < 2) {
_channel->mgInfo->botStatus = 2;
}
}
Notify::peerUpdatedDelayed(update);
}
if (_addAdminBox) _addAdminBox->onClose();
emit adminAdded();
@ -224,7 +229,7 @@ bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) {
_addAdminRequestId = 0;
if (_addAdminBox) _addAdminBox->onClose();
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") {
Ui::showLayer(new InformBox(lang(lng_channel_admins_too_much)), KeepOtherLayers);
} else if (error.type() == qstr("USER_RESTRICTED")) {
@ -728,7 +733,7 @@ void ContactsInner::changeCheckState(ContactData *data, PeerData *peer) {
_checkedContacts.insert(peer, true);
++_selCount;
} 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()) {
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) {
result += qMax(_chat->count, 1);
} else if (_channel) {
result += qMax(_channel->count, _already.size());
result += qMax(_channel->membersCount(), _already.size());
} else if (_creating == CreatingGroupGroup) {
result += 1;
}
@ -1739,7 +1744,7 @@ bool ContactsBox::creationFail(const RPCError &error) {
MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : TWidget()
, _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)
, _channel(channel)
, _filter(filter)
@ -1808,7 +1813,7 @@ void MembersInner::paintEvent(QPaintEvent *e) {
paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown);
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);
_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 (PeerData *peer = _rows[_sel]) {
Ui::hideLayer();
App::main()->showPeerProfile(peer, ShowAtUnreadMsgId);
Ui::showPeerProfile(peer);
}
}
@ -1987,7 +1992,7 @@ void MembersInner::refresh() {
} else {
_about.setText(st::boxTextFont, lng_channel_only_last_shown(lt_count, _rows.size()));
_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;
}
resize(width(), st::membersPadding.top() + _newItemHeight + _rows.size() * _rowHeight + st::membersPadding.bottom() + _aboutHeight);
@ -2003,11 +2008,11 @@ MembersFilter MembersInner::filter() const {
return _filter;
}
QMap<UserData*, bool> MembersInner::already() const {
MembersAlreadyIn MembersInner::already() const {
MembersAlreadyIn result;
for (int32 i = 0, l = _rows.size(); i < l; ++i) {
if (_rows.at(i)->isUser()) {
result.insert(_rows.at(i)->asUser(), true);
for_const (auto peer, _rows) {
if (peer->isUser()) {
result.insert(peer->asUser());
}
}
return result;
@ -2115,14 +2120,16 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
_datas.reserve(v.size());
_dates.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);
} else if (_filter == MembersFilterAdmins && _channel->adminsCount < d.vcount.v) {
_channel->adminsCount = d.vcount.v;
} else if (_filter == MembersFilterAdmins && _channel->adminsCount() < d.vcount.v) {
_channel->setAdminsCount(d.vcount.v);
if (App::main()) emit App::main()->peerUpdated(_channel);
}
App::feedUsers(d.vusers);
for (QVector<MTPChannelParticipant>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
int32 userId = 0, addedTime = 0;
MemberRole role = MemberRoleNone;
@ -2173,6 +2180,8 @@ void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result
_channel->mgInfo->lastAdmins.insert(_rows.at(i));
}
}
Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged);
}
}
if (_rows.isEmpty()) {
@ -2229,11 +2238,11 @@ void MembersInner::removeKicked() {
_dates.removeAt(index);
_roles.removeAt(index);
clearSel();
if (_filter == MembersFilterRecent && _channel->count > 1) {
--_channel->count;
if (_filter == MembersFilterRecent && _channel->membersCount() > 1) {
_channel->setMembersCount(_channel->membersCount() - 1);
if (App::main()) emit App::main()->peerUpdated(_channel);
} else if (_filter == MembersFilterAdmins && _channel->adminsCount > 1) {
--_channel->adminsCount;
} else if (_filter == MembersFilterAdmins && _channel->adminsCount() > 1) {
_channel->setAdminsCount(_channel->adminsCount() - 1);
if (App::main()) emit App::main()->peerUpdated(_channel);
}
refresh();
@ -2290,8 +2299,8 @@ void MembersBox::onScroll() {
}
void MembersBox::onAdd() {
if (_inner.filter() == MembersFilterRecent && _inner.channel()->count >= (_inner.channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) {
Ui::showLayer(new MaxInviteBox(_inner.channel()->invitationUrl), KeepOtherLayers);
if (_inner.filter() == MembersFilterRecent && _inner.channel()->membersCount() >= (_inner.channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) {
Ui::showLayer(new MaxInviteBox(_inner.channel()->inviteLink()), KeepOtherLayers);
return;
}
ContactsBox *box = new ContactsBox(_inner.channel(), _inner.filter(), _inner.already());

View file

@ -31,7 +31,7 @@ enum MembersFilter {
MembersFilterRecent,
MembersFilterAdmins,
};
typedef QMap<UserData*, bool> MembersAlreadyIn;
using MembersAlreadyIn = OrderedSet<UserData*>;
QString cantInviteError();
@ -318,7 +318,7 @@ public:
}
void clearSel();
QMap<UserData*, bool> already() const;
MembersAlreadyIn already() const;
~MembersInner();

View 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);
}

View 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;
};

View file

@ -289,7 +289,7 @@ QString Generator::valueAssignmentCode(structure::Value value) const {
} break;
case Tag::Icon: {
auto v(value.Icon());
if (v.parts.empty()) return QString();
if (v.parts.empty()) return QString("{}");
QStringList parts;
for (const auto &part : v.parts) {

View file

@ -535,8 +535,8 @@ structure::Value ParsedFile::readPointValue() {
if (tokenValue(font) == "point") {
assertNextToken(BasicType::LeftParenthesis);
auto x = readNumericValue(); assertNextToken(BasicType::Comma);
auto y = readNumericValue();
auto x = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto y = readNumericOrNumericCopyValue();
if (x.type().tag != structure::TypeTag::Pixels ||
y.type().tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "expected two px values for the point";
@ -581,8 +581,8 @@ structure::Value ParsedFile::readSizeValue() {
if (tokenValue(font) == "size") {
assertNextToken(BasicType::LeftParenthesis);
auto w = readNumericValue(); assertNextToken(BasicType::Comma);
auto h = readNumericValue();
auto w = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto h = readNumericOrNumericCopyValue();
if (w.type().tag != structure::TypeTag::Pixels ||
h.type().tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "expected two px values for the size";
@ -662,10 +662,10 @@ structure::Value ParsedFile::readMarginsValue() {
if (tokenValue(font) == "margins") {
assertNextToken(BasicType::LeftParenthesis);
auto l = readNumericValue(); assertNextToken(BasicType::Comma);
auto t = readNumericValue(); assertNextToken(BasicType::Comma);
auto r = readNumericValue(); assertNextToken(BasicType::Comma);
auto b = readNumericValue();
auto l = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto t = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto r = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto b = readNumericOrNumericCopyValue();
if (l.type().tag != structure::TypeTag::Pixels ||
t.type().tag != structure::TypeTag::Pixels ||
r.type().tag != structure::TypeTag::Pixels ||
@ -701,18 +701,10 @@ structure::Value ParsedFile::readFontValue() {
file_.putBack();
}
}
if (auto familyValue = readStringValue()) {
if (auto familyValue = readStringOrStringCopyValue()) {
family = familyValue;
} else if (auto sizeValue = readNumericValue()) {
} else if (auto sizeValue = readNumericOrNumericCopyValue()) {
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)) {
break;
} else {
@ -776,7 +768,37 @@ structure::Value ParsedFile::readCopyValue() {
if (auto variable = module_->findVariable(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 {};
}

View file

@ -98,6 +98,9 @@ private:
structure::Value readIconValue();
structure::Value readCopyValue();
structure::Value readNumericOrNumericCopyValue();
structure::Value readStringOrStringCopyValue();
structure::data::monoicon readMonoIconFields();
QString readMonoIconFilename();
@ -131,6 +134,7 @@ private:
{ "align" , { structure::TypeTag::Align } },
{ "margins" , { structure::TypeTag::Margins } },
{ "font" , { structure::TypeTag::Font } },
{ "icon" , { structure::TypeTag::Icon } },
};
};

View file

@ -74,14 +74,15 @@ bool Processor::write(const structure::Module &module) const {
QFileInfo srcFile(module.filepath());
QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module);
bool forceReGenerate = false;// !options_.rebuildDependencies;
common::ProjectInfo project = {
"codegen_style",
srcFile.fileName(),
"stdafx.h",
false,//!options_.rebuildDependencies, // forceReGenerate
forceReGenerate
};
SpriteGenerator spriteGenerator(module);
SpriteGenerator spriteGenerator(module, forceReGenerate);
if (!spriteGenerator.writeSprites()) {
return false;
}

View file

@ -48,8 +48,9 @@ constexpr int kErrorCouldNotWrite = 845;
} // namespace
SpriteGenerator::SpriteGenerator(const structure::Module &module)
SpriteGenerator::SpriteGenerator(const structure::Module &module, bool forceReGenerate)
: module_(module)
, forceReGenerate_(forceReGenerate)
, basePath_(QFileInfo(module.filepath()).dir().absolutePath()) {
}
@ -84,7 +85,7 @@ bool SpriteGenerator::writeSprites() {
}
}
QFile file(filepath);
if (file.open(QIODevice::ReadOnly)) {
if (!forceReGenerate_ && file.open(QIODevice::ReadOnly)) {
if (file.readAll() == spriteData) {
continue;
}

View file

@ -34,7 +34,7 @@ class Module;
class SpriteGenerator {
public:
SpriteGenerator(const structure::Module &module);
SpriteGenerator(const structure::Module &module, bool forceReGenerate);
SpriteGenerator(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%.
const structure::Module &module_;
bool forceReGenerate_;
QString basePath_;
QImage sprite2x_;
QList<structure::Variable> sprites_;

View file

@ -44,7 +44,6 @@ uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 };
#include <openssl/rand.h>
// Base types compile-time check
static_assert(sizeof(char) == 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");

View file

@ -33,6 +33,13 @@ T *getPointerAndReset(T *&ptr) {
return result;
}
template <typename T>
T createAndSwap(T &value) {
T result;
std::swap(result, value);
return result;
}
struct NullType {
};
@ -421,6 +428,24 @@ inline bool operator!=(std::nullptr_t a, const unique_ptr<T> &b) noexcept {
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_
#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;
typedef void(*ComponentConstruct)(void *location, Composer *composer);
typedef void(*ComponentDestruct)(void *location);
@ -1170,213 +1186,96 @@ public:
virtual R call(Args... args) const = 0;
virtual ~SharedCallback() {
}
typedef QSharedPointer<SharedCallback<R, Args...>> Ptr;
using Ptr = QSharedPointer<SharedCallback<R, Args...>>;
};
template <typename R>
template <typename R, typename... Args>
class FunctionImplementation {
public:
virtual R call() = 0;
virtual R call(Args... args) = 0;
virtual void destroy() { delete this; }
virtual ~FunctionImplementation() {}
};
template <typename R>
class NullFunctionImplementation : public FunctionImplementation<R> {
template <typename R, typename... Args>
class NullFunctionImplementation : public FunctionImplementation<R, Args...> {
public:
virtual R call() { return R(); }
virtual R call(Args... args) { return R(); }
virtual void destroy() {}
static NullFunctionImplementation<R> SharedInstance;
static NullFunctionImplementation<R, Args...> SharedInstance;
};
template <typename R>
NullFunctionImplementation<R> NullFunctionImplementation<R>::SharedInstance;
template <typename R>
class FunctionCreator {
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>
template <typename R, typename... Args>
NullFunctionImplementation<R, Args...> NullFunctionImplementation<R, Args...>::SharedInstance;
template <typename R, typename... Args>
class Function {
public:
typedef FunctionCreator<R> Creator;
static Creator Null() { return Creator(&NullFunctionImplementation<R>::SharedInstance); }
Function(const Creator &creator) : _implementation(creator.create()) {}
R call() { return _implementation->call(); }
~Function() { destroyImplementation(_implementation); }
Function() : _implementation(nullImpl()) {}
Function(FunctionImplementation<R, Args...> *implementation) : _implementation(implementation) {}
Function(const Function<R, Args...> &other) = delete;
Function<R, Args...> &operator=(const Function<R, Args...> &other) = delete;
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:
Function(const Function<R> &other);
Function<R> &operator=(const Function<R> &other);
FunctionImplementation<R> *_implementation;
static FunctionImplementation<R, Args...> *nullImpl() {
return &NullFunctionImplementation<R, Args...>::SharedInstance;
}
FunctionImplementation<R, Args...> *_implementation;
};
template <typename R>
class WrappedFunction : public FunctionImplementation<R> {
template <typename R, typename... Args>
class WrappedFunction : public FunctionImplementation<R, Args...> {
public:
typedef R(*Method)();
using Method = R(*)(Args... args);
WrappedFunction(Method method) : _method(method) {}
virtual R call() { return (*_method)(); }
virtual R call(Args... args) { return (*_method)(args...); }
private:
Method _method;
};
template <typename R>
inline FunctionCreator<R> func(R(*method)()) {
return FunctionCreator<R>(new WrappedFunction<R>(method));
template <typename R, typename... Args>
inline Function<R, Args...> func(R(*method)(Args... args)) {
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:
typedef R(I::*Method)();
using Method = R(I::*)(Args... args);
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:
O *_obj;
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>
class NullFunction1Implementation : public Function1Implementation<R, A1> {
public:
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));
template <typename O, typename I, typename R, typename... Args>
inline Function<R, Args...> func(O *obj, R(I::*method)(Args...)) {
return Function<R, Args...>(new ObjectFunction<O, I, R, Args...>(obj, method));
}

View file

@ -27,6 +27,7 @@ enum ExpandLinksMode {
ExpandLinksNone,
ExpandLinksShortened,
ExpandLinksAll,
ExpandLinksUrlOnly, // For custom urls leaves only url instead of text.
};
class ClickHandlerHost {
@ -144,9 +145,11 @@ public:
}
static void hostDestroyed(ClickHandlerHost *host) {
if (_activeHost == host) {
if (_active) (*_active).clear();
_activeHost = nullptr;
}
if (_pressedHost == host) {
if (_pressed) (*_pressed).clear();
_pressedHost = nullptr;
}
}

View file

@ -110,15 +110,22 @@ QString HiddenUrlClickHandler::getExpandedLinkText(ExpandLinksMode mode, const Q
QString result;
if (mode == ExpandLinksAll) {
result = textPart.toString() + qsl(" (") + url() + ')';
} else if (mode == ExpandLinksUrlOnly) {
result = url();
}
return result;
}
TextWithEntities HiddenUrlClickHandler::getExpandedLinkTextWithEntities(ExpandLinksMode mode, int entityOffset, const QStringRef &textPart) const {
TextWithEntities result;
result.entities.push_back({ EntityInTextCustomUrl, entityOffset, textPart.size(), url() });
if (mode == ExpandLinksAll) {
result.text = textPart.toString() + qsl(" (") + url() + ')';
if (mode == ExpandLinksUrlOnly) {
result.text = 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;
}
@ -174,9 +181,19 @@ TextWithEntities HashtagClickHandler::getExpandedLinkTextWithEntities(ExpandLink
return simpleTextWithEntity({ EntityInTextHashtag, entityOffset, textPart.size() });
}
PeerData *BotCommandClickHandler::_peer = nullptr;
UserData *BotCommandClickHandler::_bot = nullptr;
void BotCommandClickHandler::onClick(Qt::MouseButton button) const {
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;
if (auto item = App::hoveredLinkItem()) {
if (!bot) {

View file

@ -193,6 +193,8 @@ private:
};
class PeerData;
class UserData;
class BotCommandClickHandler : public TextClickHandler {
public:
BotCommandClickHandler(const QString &cmd) : _cmd(cmd) {
@ -204,14 +206,30 @@ public:
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;
protected:
QString url() const override {
return _cmd;
}
static PeerData *peerForCommand() {
return _peer;
}
static UserData *botForCommand() {
return _bot;
}
private:
QString _cmd;
static PeerData *_peer;
static UserData *_bot;
};

View 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

View 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

View 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_

View file

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/basic_types.h"
#define BETA_VERSION_MACRO (0ULL)
#define BETA_VERSION_MACRO (9049001ULL)
constexpr int AppVersion = 9049;
constexpr str_const AppVersionStr = "0.9.49";

View file

@ -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_delete_conversation), this, SLOT(onContextDeleteAndLeave()))->setEnabled(true);
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*)));
}
} else if (_menuPeer->isChat()) {
@ -662,7 +662,7 @@ bool DialogsInner::menuPeerMuted() {
void DialogsInner::onContextProfile() {
if (!_menuPeer) return;
App::main()->showPeerProfile(_menuPeer);
Ui::showPeerProfile(_menuPeer);
}
void DialogsInner::onContextToggleNotifications() {
@ -718,7 +718,7 @@ void DialogsInner::onContextDeleteAndLeaveSure() {
void DialogsInner::onContextToggleBlock() {
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)));
} else {
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) {
data.first->blocked = data.second ? UserIsBlocked : UserIsNotBlocked;
data.first->setBlockStatus(data.second ? UserData::BlockStatus::Blocked : UserData::BlockStatus::NotBlocked);
emit App::main()->peerUpdated(data.first);
}
@ -935,7 +935,7 @@ void DialogsInner::updateNotifySettings(PeerData *peer) {
void DialogsInner::peerUpdated(PeerData *peer) {
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 &params) {
if (App::app()) App::app()->mtpPause();
bool back = true;
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
_cacheUnder = params.oldContentCache;
show();
_cacheOver = App::main()->grabForShowAnimation(params);
_a_show.stop();
(back ? _cacheUnder : _cacheOver) = myGrab(this);
_scroll.hide();
_filter.hide();
_cancelSearch.hide();
_newGroup.hide();
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
int delta = st::slideShift;
if (direction == Window::SlideDirection::FromLeft) {
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();
show();
}
void DialogsWidget::step_show(float64 ms, bool timer) {
@ -1872,7 +1877,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) {
a_coordUnder.finish();
a_coordOver.finish();
a_shadow.finish();
a_progress.finish();
_cacheUnder = _cacheOver = QPixmap();
@ -1887,7 +1892,7 @@ void DialogsWidget::step_show(float64 ms, bool timer) {
} else {
a_coordUnder.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();
}
@ -2474,15 +2479,16 @@ void DialogsWidget::paintEvent(QPaintEvent *e) {
p.setClipRect(r);
}
if (_a_show.animating()) {
int retina = cIntRetinaFactor();
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.setOpacity(a_shadow.current() * st::slideFadeOut);
p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b);
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_progress.current() * st::slideFadeOut);
p.fillRect(0, 0, a_coordOver.current(), _cacheUnder.height() / retina, st::black);
p.setOpacity(1);
}
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
p.setOpacity(a_shadow.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(), 0, _cacheOver.width() / retina, _cacheOver.height() / retina), _cacheOver, QRect(0, 0, _cacheOver.width(), _cacheOver.height()));
p.setOpacity(a_progress.current());
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), _cacheOver.height() / retina), App::sprite(), st::slideShadow.rect());
return;
}
QRect above(0, 0, width(), _scroll.y());

View file

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "window/section_widget.h"
class MainWidget;
namespace Dialogs {
class Row;
@ -260,7 +262,10 @@ public:
void dialogsToUp();
void animShow(const QPixmap &bgAnimCache);
bool hasTopBarShadow() const {
return true;
}
void showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams &params);
void step_show(float64 ms, bool timer);
void destroyData();
@ -338,7 +343,7 @@ private:
Animation _a_show;
QPixmap _cacheUnder, _cacheOver;
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_shadow;
anim::fvalue a_progress;
PeerData *_searchInPeer, *_searchInMigrated;

View file

@ -20,10 +20,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#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 "mainwidget.h"
#include "application.h"
#include "core/click_handler_types.h"
#include "boxes/confirmbox.h"
#include "layerwidget.h"
#include "lang.h"
@ -237,7 +240,15 @@ void autoplayMediaInlineAsync(const FullMsgId &msgId) {
}
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) {
@ -273,6 +284,15 @@ bool hideWindowNoQuit() {
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 Notify {
@ -286,7 +306,10 @@ void userIsContactChanged(UserData *user, bool fromThisApp) {
}
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) {
@ -500,6 +523,8 @@ struct Data {
uint64 LaunchId = 0;
SingleDelayedCall HandleHistoryUpdate = { App::app(), "call_handleHistoryUpdate" };
SingleDelayedCall HandleUnreadCounterUpdate = { App::app(), "call_handleUnreadCounterUpdate" };
SingleDelayedCall HandleFileDialogQueue = { App::app(), "call_handleFileDialogQueue" };
SingleDelayedCall HandleDelayedPeerUpdates = { App::app(), "call_handleDelayedPeerUpdates" };
Adaptive::Layout AdaptiveLayout = Adaptive::NormalLayout;
bool AdaptiveForWide = true;
@ -563,6 +588,8 @@ void finish() {
DefineReadOnlyVar(Global, uint64, LaunchId);
DefineRefVar(Global, SingleDelayedCall, HandleHistoryUpdate);
DefineRefVar(Global, SingleDelayedCall, HandleUnreadCounterUpdate);
DefineRefVar(Global, SingleDelayedCall, HandleFileDialogQueue);
DefineRefVar(Global, SingleDelayedCall, HandleDelayedPeerUpdates);
DefineVar(Global, Adaptive::Layout, AdaptiveLayout);
DefineVar(Global, bool, AdaptiveForWide);

View file

@ -75,6 +75,14 @@ inline void showPeerProfile(const History *history) {
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);
inline void showPeerHistory(const PeerData *peer, MsgId msgId, bool back = false) {
showPeerHistory(peer->id, msgId, back);
@ -96,6 +104,8 @@ PeerData *getPeerForMouseAction();
bool hideWindowNoQuit();
bool skipPaintEvent(QWidget *widget, QPaintEvent *event);
} // namespace Ui
enum ClipStopperType {
@ -194,6 +204,8 @@ void finish();
DeclareReadOnlyVar(uint64, LaunchId);
DeclareRefVar(SingleDelayedCall, HandleHistoryUpdate);
DeclareRefVar(SingleDelayedCall, HandleUnreadCounterUpdate);
DeclareRefVar(SingleDelayedCall, HandleFileDialogQueue);
DeclareRefVar(SingleDelayedCall, HandleDelayedPeerUpdates);
DeclareVar(Adaptive::Layout, AdaptiveLayout);
DeclareVar(bool, AdaptiveForWide);

View file

@ -36,6 +36,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "apiwrap.h"
#include "window/top_bar_widget.h"
#include "playerwidget.h"
#include "observer_peer.h"
namespace {
@ -751,6 +752,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
if (peer->asChannel()->mgInfo->lastParticipants.indexOf(user) < 0) {
peer->asChannel()->mgInfo->lastParticipants.push_front(user);
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsAdminsOutdated;
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
}
if (user->botInfo) {
peer->asChannel()->mgInfo->bots.insert(user);
@ -769,6 +771,7 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
if (result->from()->isUser()) {
if (peer->asChannel()->mgInfo->lastParticipants.indexOf(result->from()->asUser()) < 0) {
peer->asChannel()->mgInfo->lastParticipants.push_front(result->from()->asUser());
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
}
if (result->from()->asUser()->botInfo) {
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 (peer->isMegagroup()) {
if (UserData *user = App::userLoaded(uid)) {
int32 index = peer->asChannel()->mgInfo->lastParticipants.indexOf(user);
if (auto user = App::userLoaded(uid)) {
auto channel = peer->asChannel();
auto megagroupInfo = channel->mgInfo;
int32 index = megagroupInfo->lastParticipants.indexOf(user);
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) {
--peer->asChannel()->count;
if (peer->asChannel()->membersCount() > 1) {
peer->asChannel()->setMembersCount(channel->membersCount() - 1);
} else {
peer->asChannel()->mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
peer->asChannel()->mgInfo->lastParticipantsCount = 0;
megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated;
megagroupInfo->lastParticipantsCount = 0;
}
if (peer->asChannel()->mgInfo->lastAdmins.contains(user)) {
peer->asChannel()->mgInfo->lastAdmins.remove(user);
if (peer->asChannel()->adminsCount > 1) {
--peer->asChannel()->adminsCount;
if (megagroupInfo->lastAdmins.contains(user)) {
megagroupInfo->lastAdmins.remove(user);
if (channel->adminsCount() > 1) {
channel->setAdminsCount(channel->adminsCount() - 1);
}
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::AdminsChanged);
}
peer->asChannel()->mgInfo->bots.remove(user);
if (peer->asChannel()->mgInfo->bots.isEmpty() && peer->asChannel()->mgInfo->botStatus > 0) {
peer->asChannel()->mgInfo->botStatus = -1;
megagroupInfo->bots.remove(user);
if (megagroupInfo->bots.isEmpty() && megagroupInfo->botStatus > 0) {
megagroupInfo->botStatus = -1;
}
}
}
@ -848,9 +856,10 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
} break;
case mtpc_messageActionChatEditTitle: {
const auto &d(action.c_messageActionChatEditTitle());
ChatData *chat = peer->asChat();
if (chat) chat->updateName(qs(d.vtitle), QString(), QString());
auto &d(action.c_messageActionChatEditTitle());
if (auto chat = peer->asChat()) {
chat->setName(qs(d.vtitle));
}
} break;
case mtpc_messageActionChatMigrateTo: {
@ -1019,6 +1028,9 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) {
if (prev) {
lastAuthors->push_front(adding->from()->asUser());
}
if (peer->isMegagroup()) {
Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
}
}
}
if (adding->definesReplyKeyboard()) {
@ -1194,6 +1206,7 @@ void History::addOlderSlice(const QVector<MTPMessage> &slice) {
lastAuthors->push_back(item->from()->asUser());
if (peer->isMegagroup()) {
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)) {
clear(true);
if (msgId == ShowAtTheEndMsgId) {
newLoaded = true;
}
@ -2847,14 +2861,14 @@ HistoryItem::~HistoryItem() {
}
}
RadialAnimation::RadialAnimation(AnimationCreator creator)
RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
: _firstStart(0)
, _lastStart(0)
, _lastTime(0)
, _opacity(0)
, a_arcEnd(0, 0)
, a_arcStart(0, FullArcLength)
, _animation(creator) {
, _animation(std_::move(callbacks)) {
}

View file

@ -1590,7 +1590,7 @@ protected:
class RadialAnimation {
public:
RadialAnimation(AnimationCreator creator);
RadialAnimation(AnimationCallbacks &&callbacks);
float64 opacity() const {
return _opacity;
@ -1811,9 +1811,9 @@ protected:
virtual bool dataLoaded() const = 0;
struct AnimationData {
AnimationData(AnimationCreator thumbOverCallbacks, AnimationCreator radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(thumbOverCallbacks)
, radial(radialCallbacks) {
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(std_::move(thumbOverCallbacks))
, radial(std_::move(radialCallbacks)) {
}
anim::fvalue a_thumbOver;
Animation _a_thumbOver;

View file

@ -39,6 +39,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h"
#include "apiwrap.h"
#include "window/top_bar_widget.h"
#include "observer_peer.h"
#include "playerwidget.h"
namespace {
@ -658,7 +659,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
_selected.insert(_dragItem, selStatus);
_dragSymbol = dragState.symbol;
_dragAction = Selecting;
_dragSelType = TextSelectParagraphs;
_dragSelType = TextSelectType::Paragraphs;
dragActionUpdate(_dragPos);
_trippleClickTimer.start(QApplication::doubleClickInterval());
}
@ -668,7 +669,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
request.flags = Text::StateRequest::Flag::LookupSymbol;
dragState = _dragItem->getState(_dragStartPos.x(), _dragStartPos.y(), request);
}
if (_dragSelType != TextSelectParagraphs) {
if (_dragSelType != TextSelectType::Paragraphs) {
if (App::pressedItem()) {
_dragSymbol = dragState.symbol;
bool uponSelected = (dragState.cursor == HistoryInTextCursorState);
@ -715,7 +716,7 @@ void HistoryInner::dragActionStart(const QPoint &screenPos, Qt::MouseButton butt
if (!_dragItem) {
_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");
}
drag->setMimeData(mimeData);
// We don't receive mouseReleaseEvent when drag is finished.
ClickHandler::unpressed();
drag->exec(Qt::CopyAction);
if (App::main()) App::main()->updateAfterDrag();
return;
@ -810,6 +814,9 @@ void HistoryInner::onDragExec() {
}
drag->setMimeData(mimeData);
// We don't receive mouseReleaseEvent when drag is finished.
ClickHandler::unpressed();
drag->exec(Qt::CopyAction);
if (App::main()) App::main()->updateAfterDrag();
return;
@ -906,7 +913,7 @@ void HistoryInner::dragActionFinish(const QPoint &screenPos, Qt::MouseButton but
}
_dragAction = NoDrag;
_dragItem = 0;
_dragSelType = TextSelectLetters;
_dragSelType = TextSelectType::Letters;
_widget->noSelectingScroll();
_widget->updateTopBarSelection();
}
@ -922,13 +929,13 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) {
if (!_history) return;
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;
request.flags |= Text::StateRequest::Flag::LookupSymbol;
auto dragState = _dragItem->getState(_dragStartPos.x(), _dragStartPos.y(), request);
if (dragState.cursor == HistoryInTextCursorState) {
_dragSymbol = dragState.symbol;
_dragSelType = TextSelectWords;
_dragSelType = TextSelectType::Words;
if (_dragAction == NoDrag) {
_dragAction = Selecting;
TextSelection selStatus = { dragState.symbol, dragState.symbol };
@ -1787,7 +1794,7 @@ void HistoryInner::onUpdateSelected() {
bool canSelectMany = (_history != nullptr);
if (selectingText) {
uint16 second = dragState.symbol;
if (dragState.afterSymbol && _dragSelType == TextSelectLetters) {
if (dragState.afterSymbol && _dragSelType == TextSelectType::Letters) {
++second;
}
auto selState = _dragItem->adjustSelection({ qMin(second, _dragSymbol), qMax(second, _dragSymbol) }, _dragSelType);
@ -2816,7 +2823,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
, _attachDragPhoto(this)
, _fileLoader(this, FileLoaderQueueStopTimeout)
, _a_show(animation(this, &HistoryWidget::step_show))
, _sideShadow(this, st::shadowColor)
, _topShadow(this, st::shadowColor) {
_scroll.setFocusPolicy(Qt::NoFocus);
@ -2937,7 +2943,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
_attachDragPhoto.hide();
_topShadow.hide();
_sideShadow.setVisible(!Adaptive::OneColumn());
connect(&_attachDragDocument, SIGNAL(dropped(const QMimeData*)), this, SLOT(onDocumentDrop(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) {
updateNotifySettings();
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() {
_topShadow.setVisible(_peer ? true : false);
if (!_a_show.animating()) {
_topShadow.setVisible(_peer ? true : false);
}
if (!_history || _a_show.animating()) {
_reportSpamPanel.hide();
_scroll.hide();
@ -4455,6 +4462,7 @@ void HistoryWidget::messagesReceived(PeerData *peer, const MTPmessages_Messages
} else if (_migrated) {
_migrated->clear(true);
}
_delayedShowAtRequest = 0;
_history->getReadyFor(_delayedShowAtMsgId);
if (_history->isEmpty()) {
@ -4822,7 +4830,7 @@ void HistoryWidget::onSend(bool ctrlShiftEnter, MsgId replyTo) {
void HistoryWidget::onUnblock() {
if (_unblockRequest) return;
if (!_peer || !_peer->isUser() || _peer->asUser()->blocked != UserIsBlocked) {
if (!_peer || !_peer->isUser() || !_peer->asUser()->isBlocked()) {
updateControlsVisibility();
return;
}
@ -4833,7 +4841,7 @@ void HistoryWidget::onUnblock() {
void HistoryWidget::unblockDone(PeerData *peer, const MTPBool &result, mtpRequestId req) {
if (!peer->isUser()) return;
if (_unblockRequest == req) _unblockRequest = 0;
peer->asUser()->blocked = UserIsNotBlocked;
peer->asUser()->setBlockStatus(UserData::BlockStatus::NotBlocked);
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) {
if (!peer->isUser()) return;
peer->asUser()->blocked = UserIsBlocked;
peer->asUser()->setBlockStatus(UserData::BlockStatus::Blocked);
emit App::main()->peerUpdated(peer);
}
@ -4911,12 +4919,14 @@ void HistoryWidget::onBroadcastSilentChange() {
}
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);
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) {
@ -4993,15 +5003,15 @@ MsgId HistoryWidget::msgId() const {
return _showAtMsgId;
}
void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back) {
void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window::SectionSlideParams &params) {
if (App::app()) App::app()->mtpPause();
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
(back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache;
(back ? _cacheUnder : _cacheOver) = myGrab(this);
App::main()->topBar()->stopAnim();
(back ? _cacheTopBarUnder : _cacheTopBarOver) = myGrab(App::main()->topBar());
_cacheUnder = params.oldContentCache;
show();
_topShadow.setVisible(params.withTopBarShadow ? false : true);
_cacheOver = App::main()->grabForShowAnimation(params);
App::main()->topBar()->startAnim();
_topShadow.setVisible(params.withTopBarShadow ? true : false);
_scroll.hide();
_kbScroll.hide();
@ -5023,18 +5033,26 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
_botStart.hide();
_joinChannel.hide();
_muteUnmute.hide();
_topShadow.hide();
if (_pinnedBar) {
_pinnedBar->shadow.hide();
_pinnedBar->cancel.hide();
}
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
int delta = st::slideShift;
if (direction == Window::SlideDirection::FromLeft) {
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();
App::main()->topBar()->update();
activate();
}
@ -5042,13 +5060,12 @@ void HistoryWidget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
if (dt >= 1) {
_a_show.stop();
_sideShadow.setVisible(!Adaptive::OneColumn());
_topShadow.setVisible(_peer ? true : false);
a_coordUnder.finish();
a_coordOver.finish();
a_shadow.finish();
_cacheUnder = _cacheOver = _cacheTopBarUnder = _cacheTopBarOver = QPixmap();
a_progress.finish();
_cacheUnder = _cacheOver = QPixmap();
App::main()->topBar()->stopAnim();
doneShow();
@ -5056,7 +5073,7 @@ void HistoryWidget::step_show(float64 ms, bool timer) {
} else {
a_coordUnder.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();
@ -5081,14 +5098,12 @@ void HistoryWidget::doneShow() {
}
void HistoryWidget::updateAdaptiveLayout() {
_sideShadow.setVisible(!Adaptive::OneColumn());
update();
}
void HistoryWidget::animStop() {
if (!_a_show.animating()) return;
_a_show.stop();
_sideShadow.setVisible(!Adaptive::OneColumn());
_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) {
if (!i->isLocalFile()) return DragStateNone;
QString file(i->toLocalFile());
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
auto file = psConvertFileUrl(*i);
QFileInfo info(file);
if (info.isDir()) return DragStateNone;
@ -5569,7 +5583,7 @@ bool HistoryWidget::isBotStart() const {
}
bool HistoryWidget::isBlocked() const {
return _peer && _peer->isUser() && _peer->asUser()->blocked == UserIsBlocked;
return _peer && _peer->isUser() && _peer->asUser()->isBlocked();
}
bool HistoryWidget::isJoinChannel() const {
@ -5769,9 +5783,15 @@ void HistoryWidget::onForwardHere() {
void HistoryWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) {
if (_a_show.animating()) {
p.drawPixmap(a_coordUnder.current(), 0, _cacheTopBarUnder);
p.drawPixmap(a_coordOver.current(), 0, _cacheTopBarOver);
p.setOpacity(a_shadow.current());
int retina = cIntRetinaFactor();
if (a_coordOver.current() > 0) {
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());
return;
}
@ -5806,7 +5826,7 @@ void HistoryWidget::topBarClick() {
if (Adaptive::OneColumn()) {
Ui::showChatsList();
} 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()) {
if (_peer->isMegagroup() && _peer->asChannel()->count > 0 && _peer->asChannel()->count <= Global::ChatSizeMax()) {
if (_peer->asChannel()->mgInfo->lastParticipants.size() < _peer->asChannel()->count || _peer->asChannel()->lastParticipantsCountOutdated()) {
if (_peer->isMegagroup() && _peer->asChannel()->membersCount() > 0 && _peer->asChannel()->membersCount() <= Global::ChatSizeMax()) {
if (_peer->asChannel()->mgInfo->lastParticipants.size() < _peer->asChannel()->membersCount() || _peer->asChannel()->lastParticipantsCountOutdated()) {
if (App::api()) App::api()->requestLastParticipants(_peer->asChannel());
}
int32 onlineCount = 0;
@ -5852,12 +5872,12 @@ void HistoryWidget::updateOnlineDisplay(int32 x, int32 w) {
}
}
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 {
text = lng_chat_status_members(lt_count, _peer->asChannel()->count);
text = lng_chat_status_members(lt_count, _peer->asChannel()->membersCount());
}
} 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) {
@ -6494,8 +6514,6 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
_sideShadow.resize(st::lineWidth, height());
_sideShadow.moveToLeft(0, 0);
}
void HistoryWidget::itemRemoved(HistoryItem *item) {
@ -6671,7 +6689,8 @@ void HistoryWidget::updateListSize(bool initial, bool loadedDown, const ScrollCh
}
} else {
}
if (toY > _scroll.scrollTopMax()) toY = _scroll.scrollTopMax();
auto scrollMax = _scroll.scrollTopMax();
accumulate_min(toY, scrollMax);
if (_scroll.scrollTop() == toY) {
visibleAreaUpdated();
} else {
@ -7041,7 +7060,6 @@ bool HistoryWidget::pinnedMsgVisibilityUpdated() {
}
connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide()));
_reportSpamPanel.raise();
_sideShadow.raise();
_topShadow.raise();
updatePinnedBar();
result = true;
@ -7679,7 +7697,7 @@ void HistoryWidget::peerUpdated(PeerData *data) {
if (App::api()) {
if (data->isChat() && data->asChat()->noParticipantInfo()) {
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);
} else if (data->isMegagroup() && !data->asChannel()->mgInfo->botStatus) {
App::api()->requestBots(data->asChannel());
@ -7723,6 +7741,7 @@ void HistoryWidget::onDeleteSelected() {
}
void HistoryWidget::onDeleteSelectedSure() {
Ui::hideLayer();
if (!_list) return;
SelectedItemSet sel;
@ -7740,7 +7759,6 @@ void HistoryWidget::onDeleteSelectedSure() {
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
i.value()->destroy();
}
Ui::hideLayer();
for (QMap<PeerData*, QVector<MTPint> >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
App::main()->deleteMessages(i.key(), i.value());
@ -7748,6 +7766,8 @@ void HistoryWidget::onDeleteSelectedSure() {
}
void HistoryWidget::onDeleteContextSure() {
Ui::hideLayer();
HistoryItem *item = App::contextItem();
if (!item || item->type() != HistoryItemMsg) {
return;
@ -7757,12 +7777,11 @@ void HistoryWidget::onDeleteContextSure() {
History *h = item->history();
bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item);
item->destroy();
if (!wasOnServer && wasLast && !h->lastMsg) {
App::main()->checkPeerHistory(h->peer);
}
Ui::hideLayer();
if (wasOnServer) {
App::main()->deleteMessages(h->peer, toDelete);
}
@ -8117,20 +8136,23 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
if (r != rect()) {
p.setClipRect(r);
}
bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
if (_a_show.animating()) {
int retina = cIntRetinaFactor();
int inCacheTop = hasTopBar ? st::topBarHeight : 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.setOpacity(a_shadow.current() * st::slideFadeOut);
p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b);
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_progress.current() * st::slideFadeOut);
p.fillRect(0, 0, a_coordOver.current(), height(), st::black);
p.setOpacity(1);
}
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
p.setOpacity(a_shadow.current());
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina));
p.setOpacity(a_progress.current());
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
return;
}
bool hasTopBar = !App::main()->topBar()->isHidden(), hasPlayer = !App::main()->player()->isHidden();
QRect fill(0, 0, width(), App::main()->height());
int fromy = (hasTopBar ? (-st::topBarHeight) : 0) + (hasPlayer ? (-st::playerHeight) : 0), x = 0, y = 0;
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) {
if (!i->isLocalFile()) return QStringList();
QString file(i->toLocalFile());
if (file.startsWith(qsl("/.file/id="))) file = psConvertFileUrl(file);
auto file = psConvertFileUrl(*i);
QFileInfo info(file);
uint64 s = info.size();

View file

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "dropdown.h"
#include "history/history_common.h"
#include "history/field_autocomplete.h"
#include "window/section_widget.h"
namespace InlineBots {
namespace Layout {
@ -205,7 +206,7 @@ private:
Selecting = 0x04,
};
DragAction _dragAction = NoDrag;
TextSelectType _dragSelType = TextSelectLetters;
TextSelectType _dragSelType = TextSelectType::Letters;
QPoint _dragStartPos, _dragPos;
HistoryItem *_dragItem = nullptr;
HistoryCursorState _dragCursorState = HistoryDefaultCursorState;
@ -570,7 +571,10 @@ public:
void setMsgId(MsgId showAtMsgId);
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 &params);
void step_show(float64 ms, bool timer);
void animStop();
@ -652,14 +656,17 @@ public:
bool contentOverlapped(const QRect &globalRect);
void grabStart() override {
_sideShadow.hide();
_inGrab = true;
resizeEvent(0);
}
void grapWithoutTopBarShadow() {
grabStart();
_topShadow.hide();
}
void grabFinish() override {
_sideShadow.setVisible(!Adaptive::OneColumn());
_inGrab = false;
resizeEvent(0);
_topShadow.show();
}
bool isItemVisible(HistoryItem *item);
@ -1077,9 +1084,9 @@ private:
int32 _titlePeerTextWidth = 0;
Animation _a_show;
QPixmap _cacheUnder, _cacheOver, _cacheTopBarUnder, _cacheTopBarOver;
QPixmap _cacheUnder, _cacheOver;
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_shadow;
anim::fvalue a_progress;
QTimer _scrollTimer;
int32 _scrollDelta = 0;
@ -1094,7 +1101,7 @@ private:
bool _saveDraftText = false;
QTimer _saveDraftTimer, _saveCloudDraftTimer;
PlainShadow _sideShadow, _topShadow;
PlainShadow _topShadow;
bool _inGrab = false;
};

View file

@ -216,9 +216,8 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (_delete && p == _delete) {
bool wasactive = (_state & StateFlag::DeleteOver);
if (active != wasactive) {
float64 from = active ? 0 : 1, to = active ? 1 : 0;
EnsureAnimation(_a_deleteOver, from, func(this, &Gif::update));
_a_deleteOver.start(to, st::stickersRowDuration);
auto from = active ? 0. : 1., to = active ? 1. : 0.;
START_ANIMATION(_a_deleteOver, func(this, &Gif::update), from, to, st::stickersRowDuration, anim::linear);
if (active) {
_state |= StateFlag::DeleteOver;
} else {
@ -231,9 +230,8 @@ void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (active != wasactive) {
if (!getShownDocument()->loaded()) {
ensureAnimation();
float64 from = active ? 0 : 1, to = active ? 1 : 0;
EnsureAnimation(_animation->_a_over, from, func(this, &Gif::update));
_animation->_a_over.start(to, st::stickersRowDuration);
auto from = active ? 0. : 1., to = active ? 1. : 0.;
START_ANIMATION(_animation->_a_over, func(this, &Gif::update), from, to, st::stickersRowDuration, anim::linear);
}
if (active) {
_state |= StateFlag::Over;
@ -413,9 +411,8 @@ void Sticker::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
if (active != _active) {
_active = active;
float64 from = _active ? 0 : 1, to = _active ? 1 : 0;
EnsureAnimation(_a_over, from, func(this, &Sticker::update));
_a_over.start(to, st::stickersRowDuration);
auto from = active ? 0. : 1., to = active ? 1. : 0.;
START_ANIMATION(_a_over, func(this, &Sticker::update), from, to, st::stickersRowDuration, anim::linear);
}
}
ItemBase::clickHandlerActiveChanged(p, active);

View file

@ -107,9 +107,9 @@ private:
void clipCallback(ClipReaderNotification notification);
struct AnimationData {
AnimationData(AnimationCreator creator)
AnimationData(AnimationCallbacks &&callbacks)
: over(false)
, radial(creator) {
, radial(std_::move(callbacks)) {
}
bool over;
FloatAnimation _a_over;
@ -268,9 +268,9 @@ private:
}
struct AnimationData {
AnimationData(AnimationCreator thumbOverCallbacks, AnimationCreator radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(thumbOverCallbacks)
, radial(radialCallbacks) {
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(std_::move(thumbOverCallbacks))
, radial(std_::move(radialCallbacks)) {
}
anim::fvalue a_thumbOver;
Animation _a_thumbOver;

View file

@ -50,7 +50,7 @@ IntroPhone::IntroPhone(IntroWidget *parent) : IntroStep(parent)
, country(this, st::introCountry)
, phone(this, st::inpIntroPhone)
, 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)
, sentRequest(0) {
setVisible(false);

View file

@ -20,9 +20,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <QtWidgets/QWidget>
#include "ui/flatbutton.h"
#include "ui/countryinput.h"
#include "ui/flatlabel.h"
#include "intro/introwidget.h"
class IntroPhone final : public IntroStep {

View file

@ -27,7 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "langloaderplain.h"
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())
, _next(this, lang(lng_start_msgs), st::btnIntroNext) {
_changeLang.hide();

View file

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "intro/introwidget.h"
#include "ui/flatlabel.h"
class IntroStart final : public IntroStep {
public:

View file

@ -180,7 +180,7 @@ void IntroWidget::animShow(const QPixmap &bgAnimCache, bool back) {
step()->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_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
_a_show.start();

View file

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "serialize/serialize_document.h"
#include "serialize/serialize_common.h"
#include "observer_peer.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "lang.h"
@ -3412,7 +3413,7 @@ namespace Local {
UserData *user = peer->asUser();
// 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
if (AppVersion >= 9012) {
@ -3424,13 +3425,13 @@ namespace Local {
} else if (peer->isChat()) {
ChatData *chat = peer->asChat();
// name + count + date + version + admin + forbidden + left + invitationUrl
result += Serialize::stringSize(chat->name) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(chat->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->inviteLink());
} else if (peer->isChannel()) {
ChannelData *channel = peer->asChannel();
// name + access + date + version + forbidden + flags + invitationUrl
result += Serialize::stringSize(channel->name) + sizeof(quint64) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + Serialize::stringSize(channel->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->inviteLink());
}
return result;
}
@ -3441,7 +3442,7 @@ namespace Local {
if (peer->isUser()) {
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) {
stream << qint32(user->flags);
}
@ -3455,12 +3456,12 @@ namespace Local {
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 << qint32(chat->isForbidden ? 1 : 0) << qint32(flagsData) << chat->invitationUrl;
stream << qint32(chat->isForbidden ? 1 : 0) << qint32(flagsData) << chat->inviteLink();
} else if (peer->isChannel()) {
ChannelData *channel = peer->asChannel();
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();
if (!wasLoaded) {
user->setPhone(phone);
user->setName(first, last, pname, username);
user->access = access;
@ -3519,9 +3521,9 @@ namespace Local {
} else if (result->isChat()) {
ChatData *chat = result->asChat();
QString name, invitationUrl;
QString name, inviteLink;
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) {
flags = flagsData;
@ -3530,14 +3532,14 @@ namespace Local {
flags = (flagsData == 1) ? MTPDchat::Flags(MTPDchat::Flag::f_left) : MTPDchat::Flags(0);
}
if (!wasLoaded) {
chat->updateName(name, QString(), QString());
chat->setName(name);
chat->count = count;
chat->date = date;
chat->version = version;
chat->creator = creator;
chat->isForbidden = (forbidden == 1);
chat->flags = MTPDchat::Flags(flags);
chat->invitationUrl = invitationUrl;
chat->setInviteLink(inviteLink);
chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id)));
chat->inputChat = MTP_int(peerToChat(chat->id));
@ -3547,19 +3549,19 @@ namespace Local {
} else if (result->isChannel()) {
ChannelData *channel = result->asChannel();
QString name, invitationUrl;
QString name, inviteLink;
quint64 access;
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) {
channel->updateName(name, QString(), QString());
channel->setName(name, QString());
channel->access = access;
channel->date = date;
channel->version = version;
channel->isForbidden = (forbidden == 1);
channel->flags = MTPDchannel::Flags(flags);
channel->invitationUrl = invitationUrl;
channel->setInviteLink(inviteLink);
channel->input = MTP_inputPeerChannel(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);
peers.push_back(peer);
}
App::emitPeerUpdated();
if (App::api()) App::api()->requestPeers(peers);
}

File diff suppressed because it is too large Load diff

View file

@ -33,6 +33,9 @@ class PeerAvatarButton;
namespace Window {
class TopBarWidget;
class SectionMemento;
class SectionWidget;
struct SectionSlideParams;
} // namespace Window
class MainWindow;
@ -40,7 +43,6 @@ class ApiWrap;
class ConfirmBox;
class DialogsWidget;
class HistoryWidget;
class ProfileWidget;
class OverviewWidget;
class PlayerWidget;
class HistoryHider;
@ -48,7 +50,7 @@ class Dropdown;
enum StackItemType {
HistoryStackItem,
ProfileStackItem,
SectionStackItem,
OverviewStackItem,
};
@ -64,8 +66,9 @@ public:
class StackItemHistory : public StackItem {
public:
StackItemHistory(PeerData *peer, MsgId msgId, QList<MsgId> replyReturns) : StackItem(peer),
msgId(msgId), replyReturns(replyReturns) {
StackItemHistory(PeerData *peer, MsgId msgId, QList<MsgId> replyReturns) : StackItem(peer)
, msgId(msgId)
, replyReturns(replyReturns) {
}
StackItemType type() const {
return HistoryStackItem;
@ -74,19 +77,29 @@ msgId(msgId), replyReturns(replyReturns) {
QList<MsgId> replyReturns;
};
class StackItemProfile : public StackItem {
class StackItemSection : public StackItem {
public:
StackItemProfile(PeerData *peer, int32 lastScrollTop) : StackItem(peer), lastScrollTop(lastScrollTop) {
}
StackItemSection(std_::unique_ptr<Window::SectionMemento> &&memento);
~StackItemSection();
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 {
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 {
return OverviewStackItem;
@ -173,8 +186,6 @@ public:
void startFull(const MTPVector<MTPUser> &users);
bool started();
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);
@ -210,14 +221,14 @@ public:
PeerData *activePeer();
MsgId activeMsgId();
PeerData *profilePeer();
PeerData *overviewPeer();
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 showBackFromStack();
void orderWidgets();
QRect historyRect() const;
QPixmap grabForShowAnimation(const Window::SectionSlideParams &params);
void onSendFileConfirm(const FileLoadResultPtr &file, bool ctrlShiftEnter);
void onSendFileCancel(const FileLoadResultPtr &file);
@ -262,6 +273,7 @@ public:
void deleteMessages(PeerData *peer, const QVector<MTPint> &ids);
void deletedContact(UserData *user, const MTPcontacts_Link &result);
void deleteConversation(PeerData *peer, bool deleteHistory = true);
void deleteAndExit(ChatData *chat);
void clearHistory(PeerData *peer);
void deleteAllFromUser(ChannelData *channel, UserData *from);
@ -313,7 +325,6 @@ public:
void itemEdited(HistoryItem *item);
void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
void peerUsernameChanged(PeerData *peer);
void checkLastUpdate(bool afterSleep);
void showAddContact();
@ -483,10 +494,9 @@ public slots:
void ui_autoplayMediaInlineAsync(qint32 channelId, qint32 msgId);
private:
void sendReadRequest(PeerData *peer, MsgId upTo);
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);
void readRequestDone(PeerData *peer);
@ -496,6 +506,15 @@ private:
void saveCloudDraftDone(PeerData *peer, const MTPBool &result, 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;
uint64 failedObjId = 0;
@ -565,9 +584,11 @@ private:
int _dialogsWidth = st::dlgMinWidth;
PlainShadow _sideShadow;
ChildWidget<DialogsWidget> _dialogs;
ChildWidget<HistoryWidget> _history;
ChildWidget<ProfileWidget> _profile = { nullptr };
ChildWidget<Window::SectionWidget> _wideSection = { nullptr };
ChildWidget<OverviewWidget> _overview = { nullptr };
ChildWidget<PlayerWidget> _player;
ChildWidget<Window::TopBarWidget> _topBar;

View file

@ -35,9 +35,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h"
#include "boxes/contactsbox.h"
#include "boxes/addcontactbox.h"
#include "observer_peer.h"
#include "autoupdater.h"
#include "mediaview.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()) {
set(text, reconnect);
@ -435,7 +437,7 @@ void MainWindow::init() {
connect(windowHandle(), SIGNAL(activeChanged()), this, SLOT(checkHistoryActivation()), Qt::QueuedConnection);
QPalette p(palette());
p.setColor(QPalette::Window, st::wndBG->c);
p.setColor(QPalette::Window, st::windowBg->c);
setPalette(p);
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) {
History *h = (main && App::userLoaded(ServiceUserId)) ? App::history(ServiceUserId) : 0;
if (!h || (!force && h->isEmpty())) {
@ -1374,7 +1372,7 @@ void MainWindow::onClearFailed(int task, void *manager) {
}
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;
@ -1394,7 +1392,7 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) {
return;
}
} else {
App::wnd()->getNotifySetting(MTP_inputNotifyPeer(notifyByFrom->input));
App::api()->requestNotifySetting(notifyByFrom);
}
} else {
history->popNotification(item);
@ -1403,9 +1401,9 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) {
}
} else {
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()) {
haveSetting = false;
@ -1420,6 +1418,7 @@ void MainWindow::notifySchedule(History *history, HistoryItem *item) {
} else if (cOtherOnline() >= t) {
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;
notifyWhenAlerts[history].insert(when, notifyByFrom);
@ -1882,8 +1881,15 @@ void MainWindow::sendPaths() {
void MainWindow::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) {
if (main) main->mediaOverviewUpdated(peer, type);
if (!_mediaView || _mediaView->isHidden()) return;
_mediaView->mediaOverviewUpdated(peer, type);
if (_mediaView && !_mediaView->isHidden()) {
_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) {

View file

@ -156,7 +156,6 @@ public:
void checkAutoLockIn(int msec);
void setupIntro(bool anim);
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 sendServiceHistoryRequest();
void showDelayedServiceMsgs();

View file

@ -1875,7 +1875,7 @@ void MediaView::mouseReleaseEvent(QMouseEvent *e) {
if (_over == OverName && _down == OverName) {
if (App::wnd() && _from) {
close();
if (App::main()) App::main()->showPeerProfile(_from);
Ui::showPeerProfile(_from);
}
} else if (_over == OverDate && _down == OverDate) {
onToMessage();

View file

@ -22,7 +22,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mtproto/core_types.h"
#include "mtproto/session.h"
#include "mtproto/file_download.h"
namespace MTP {

View file

@ -208,6 +208,7 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray &
}
emit App::wnd()->imageLoaded();
emit progress(this);
FileDownload::internal::notifyImageLoaded();
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)));
}
emit progress(this);
if (_complete) {
FileDownload::internal::notifyImageLoaded();
}
loadNext();
}
@ -678,6 +682,7 @@ void webFileLoader::onFinished(const QByteArray &data) {
Local::writeWebFile(_url, _data);
}
emit progress(this);
FileDownload::internal::notifyImageLoaded();
loadNext();
}
@ -1089,3 +1094,25 @@ namespace MTP {
++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
}

View file

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "core/observer.h"
namespace MTP {
void clearLoaderPriorities();
}
@ -391,3 +393,21 @@ static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation<WebL
void reinitWebLoadManager();
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

View 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

View 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

View file

@ -611,6 +611,9 @@ void OverviewInner::onDragExec() {
mimeData->setData(qsl("application/x-td-forward-selected"), "1");
}
drag->setMimeData(mimeData);
// We don't receive mouseReleaseEvent when drag is finished.
ClickHandler::unpressed();
drag->exec(Qt::CopyAction);
if (App::main()) App::main()->updateAfterDrag();
return;
@ -639,6 +642,9 @@ void OverviewInner::onDragExec() {
}
drag->setMimeData(mimeData);
// We don't receive mouseReleaseEvent when drag is finished.
ClickHandler::unpressed();
drag->exec(Qt::CopyAction);
if (App::main()) App::main()->updateAfterDrag();
return;
@ -1909,7 +1915,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp
, _scrollSetAfterShow(0)
, _scrollDelta(0)
, _selCount(0)
, _sideShadow(this, st::shadowColor)
, _topShadow(this, st::shadowColor)
, _inGrab(false) {
_scroll.setFocusPolicy(Qt::NoFocus);
@ -1917,8 +1922,6 @@ OverviewWidget::OverviewWidget(QWidget *parent, PeerData *peer, MediaOverviewTyp
_scroll.move(0, 0);
_inner.move(0, 0);
_sideShadow.setVisible(!Adaptive::OneColumn());
updateScrollColors();
_scroll.show();
@ -1970,8 +1973,6 @@ void OverviewWidget::resizeEvent(QResizeEvent *e) {
_topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth);
_topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0);
_sideShadow.resize(st::lineWidth, height());
_sideShadow.moveToLeft(0, 0);
}
void OverviewWidget::paintEvent(QPaintEvent *e) {
@ -1979,14 +1980,16 @@ void OverviewWidget::paintEvent(QPaintEvent *e) {
Painter p(this);
if (_a_show.animating()) {
int retina = cIntRetinaFactor();
int inCacheTop = st::topBarHeight;
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.setOpacity(a_shadow.current() * st::slideFadeOut);
p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b);
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_progress.current() * st::slideFadeOut);
p.fillRect(0, 0, a_coordOver.current(), height(), st::black);
p.setOpacity(1);
}
p.drawPixmap(a_coordOver.current(), 0, _cacheOver);
p.setOpacity(a_shadow.current());
p.drawPixmap(QRect(a_coordOver.current(), 0, _cacheOver.width() / retina, height()), _cacheOver, QRect(0, inCacheTop * retina, _cacheOver.width(), height() * retina));
p.setOpacity(a_progress.current());
p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow.rect());
return;
}
@ -2012,9 +2015,15 @@ void OverviewWidget::scrollReset() {
void OverviewWidget::paintTopBar(Painter &p, float64 over, int32 decreaseWidth) {
if (_a_show.animating()) {
p.drawPixmap(a_coordUnder.current(), 0, _cacheTopBarUnder);
p.drawPixmap(a_coordOver.current(), 0, _cacheTopBarOver);
p.setOpacity(a_shadow.current());
int retina = cIntRetinaFactor();
if (a_coordOver.current() > 0) {
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());
return;
}
@ -2119,44 +2128,54 @@ void OverviewWidget::fastShow(bool back, int32 lastScrollTop) {
if (App::app()) App::app()->mtpUnpause();
}
void OverviewWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) {
if (App::app()) App::app()->mtpPause();
(back ? _cacheOver : _cacheUnder) = bgAnimCache;
(back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache;
void OverviewWidget::setLastScrollTop(int lastScrollTop) {
resizeEvent(0);
_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 &params) {
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();
_scrollSetAfterShow = _scroll.scrollTop();
_scroll.hide();
_topShadow.hide();
a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
int delta = st::slideShift;
if (direction == Window::SlideDirection::FromLeft) {
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();
show();
App::main()->topBar()->update();
_inner.activate();
activate();
}
void OverviewWidget::step_show(float64 ms, bool timer) {
float64 dt = ms / st::slideDuration;
if (dt >= 1) {
_a_show.stop();
_sideShadow.setVisible(!Adaptive::OneColumn());
_topShadow.show();
a_coordUnder.finish();
a_coordOver.finish();
a_shadow.finish();
_cacheUnder = _cacheOver = _cacheTopBarUnder = _cacheTopBarOver = QPixmap();
a_progress.finish();
_cacheUnder = _cacheOver = QPixmap();
App::main()->topBar()->stopAnim();
doneShow();
@ -2165,7 +2184,7 @@ void OverviewWidget::step_show(float64 ms, bool timer) {
} else {
a_coordUnder.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();
@ -2173,10 +2192,6 @@ void OverviewWidget::step_show(float64 ms, bool timer) {
}
}
void OverviewWidget::updateAdaptiveLayout() {
_sideShadow.setVisible(!Adaptive::OneColumn());
}
void OverviewWidget::doneShow() {
_scroll.show();
_scroll.scrollToY(_scrollSetAfterShow);
@ -2308,6 +2323,8 @@ void OverviewWidget::onDeleteSelected() {
}
void OverviewWidget::onDeleteSelectedSure() {
Ui::hideLayer();
SelectedItemSet sel;
_inner.fillSelectedItems(sel);
if (sel.isEmpty()) return;
@ -2323,7 +2340,6 @@ void OverviewWidget::onDeleteSelectedSure() {
for (SelectedItemSet::const_iterator i = sel.cbegin(), e = sel.cend(); i != e; ++i) {
i.value()->destroy();
}
Ui::hideLayer();
for (QMap<PeerData*, QVector<MTPint> >::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) {
App::main()->deleteMessages(i.key(), i.value());
@ -2331,6 +2347,8 @@ void OverviewWidget::onDeleteSelectedSure() {
}
void OverviewWidget::onDeleteContextSure() {
Ui::hideLayer();
HistoryItem *item = App::contextItem();
if (!item || item->type() != HistoryItemMsg) {
return;
@ -2340,12 +2358,11 @@ void OverviewWidget::onDeleteContextSure() {
History *h = item->history();
bool wasOnServer = (item->id > 0), wasLast = (h->lastMsg == item);
item->destroy();
if (!wasOnServer && wasLast && !h->lastMsg) {
App::main()->checkPeerHistory(h->peer);
}
Ui::hideLayer();
if (wasOnServer) {
App::main()->deleteMessages(h->peer, toDelete);
}

View file

@ -20,6 +20,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "window/section_widget.h"
namespace Overview {
namespace Layout {
@ -276,10 +278,13 @@ public:
int32 countBestScroll() const;
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 &params);
void step_show(float64 ms, bool timer);
void updateAdaptiveLayout();
void doneShow();
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
@ -300,14 +305,17 @@ public:
void updateAfterDrag();
void grabStart() override {
_sideShadow.hide();
_inGrab = true;
resizeEvent(0);
}
void grapWithoutTopBarShadow() {
grabStart();
_topShadow.hide();
}
void grabFinish() override {
_sideShadow.setVisible(!Adaptive::OneColumn());
_inGrab = false;
resizeEvent(0);
_topShadow.show();
}
void rpcClear() override {
_inner.rpcClear();
@ -343,9 +351,9 @@ private:
QString _header;
Animation _a_show;
QPixmap _cacheUnder, _cacheOver, _cacheTopBarUnder, _cacheTopBarOver;
QPixmap _cacheUnder, _cacheOver;
anim::ivalue a_coordUnder, a_coordOver;
anim::fvalue a_shadow;
anim::fvalue a_progress;
int32 _scrollSetAfterShow;
@ -354,7 +362,7 @@ private:
int32 _selCount;
PlainShadow _sideShadow, _topShadow;
PlainShadow _topShadow;
bool _inGrab;
};

View file

@ -119,7 +119,7 @@ void PasscodeWidget::animShow(const QPixmap &bgAnimCache, bool back) {
(back ? _cacheUnder : _cacheOver) = myGrab(this);
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_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1);
_a_show.start();

View file

@ -33,12 +33,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
, _a_state(animation(this, &PlayerWidget::step_state))
, _a_progress(animation(this, &PlayerWidget::step_progress))
, _sideShadow(this, st::shadowColor) {
, _a_progress(animation(this, &PlayerWidget::step_progress)) {
resize(st::wndMinWidth, st::playerHeight);
setMouseTracking(true);
memset(_stateHovers, 0, sizeof(_stateHovers));
_sideShadow.setVisible(!Adaptive::OneColumn());
}
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 {
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()));
_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();
}

View file

@ -52,7 +52,6 @@ public:
void clearSelection();
void mediaOverviewUpdated(PeerData *peer, MediaOverviewType type);
void updateAdaptiveLayout();
bool seekingSong(const SongMsgId &song) const;
@ -136,6 +135,4 @@ private:
anim::fvalue a_loadProgress = { 0., 0. };
Animation _a_progress;
PlainShadow _sideShadow;
};

View 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;
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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 &copyText) {
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

View 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 &copyText);
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

View 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

View 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

View 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

Some files were not shown because too many files have changed in this diff Show more