diff --git a/Telegram/BuildWin.bat b/Telegram/BuildWin.bat index 2e3434cef..a5bd2ef0d 100644 --- a/Telegram/BuildWin.bat +++ b/Telegram/BuildWin.bat @@ -28,19 +28,21 @@ if exist ..\Win32\Deploy\deploy\%AppVersionStrMajor%\%AppVersionStr%\ goto error if exist ..\Win32\Deploy\deploy\%AppVersionStrMajor%\%AppVersionStr%.dev\ goto error_exist2 if exist ..\Win32\Deploy\tupdate%AppVersion% goto error_exist3 -copy ./SourceFiles/telegram.qrc /B+ ,,/Y +cd SourceFiles\ +copy telegram.qrc /B+,,/Y +cd ..\ if %errorlevel% neq 0 goto error cd ..\ MSBuild Telegram.sln /property:Configuration=Deploy -if %errorlevel% neq 0 goto error +if %errorlevel% neq 0 goto error0 echo . echo Version %AppVersionStr%%DevPostfix% build successfull! Preparing.. echo . set "PATH=%PATH%;C:\Program Files\7-Zip;C:\Program Files (x86)\Inno Setup 5" -cd ..\Win32\Deploy +cd Win32\Deploy\ call ..\..\..\TelegramPrivate\Sign.bat Telegram.exe if %errorlevel% neq 0 goto error1 @@ -71,7 +73,7 @@ move tsetup.%AppVersionStr%%DevPostfix%.exe deploy\%AppVersionStrMajor%\%AppVers move tupdate%AppVersion% deploy\%AppVersionStrMajor%\%AppVersionStr%%DevPostfix%\ if %errorlevel% neq 0 goto error1 -cd deploy\%AppVersionStrMajor%\%AppVersionStr%%DevPostfix% +cd deploy\%AppVersionStrMajor%\%AppVersionStr%%DevPostfix%\ 7z a -mx9 tportable.%AppVersionStr%%DevPostfix%.zip Telegram\ if %errorlevel% neq 0 goto error2 @@ -97,13 +99,15 @@ xcopy Updater.pdb Z:\TBuild\tother\tsetup\%AppVersionStrMajor%\%AppVersionStr%%D echo Version %AppVersionStr%%DevPostfix% deployed successfully! -cd ..\..\..\..\..\Telegram +cd ..\..\..\..\..\Telegram\ goto eof :error2 -cd ..\..\.. +cd ..\..\..\ :error1 -cd ..\..\Telegram +cd ..\..\ +:error0 +cd Telegram\ goto error :error_exist1 diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index fbcc479f9..a974f2729 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -95,6 +95,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_channel_status" = "channel"; +"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."; "lng_deleted" = "Unknown"; @@ -354,12 +357,13 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_profile_chat_unaccessible" = "Group is unaccessible"; "lng_topbar_info" = "Info"; "lng_profile_about_section" = "About"; +"lng_profile_description_section" = "Description"; "lng_profile_settings_section" = "Settings"; "lng_profile_actions_section" = "Actions"; "lng_profile_bot_settings" = "Settings"; "lng_profile_bot_help" = "Help"; "lng_profile_create_public_link" = "Create public link"; -"lng_profile_edit_public_link" = "Edit link"; +"lng_profile_edit_public_link" = "Edit public link"; "lng_profile_participants_section" = "Members"; "lng_profile_info" = "Contact info"; "lng_profile_group_info" = "Group info"; @@ -384,8 +388,10 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_profile_set_group_photo" = "Set Photo"; "lng_profile_add_participant" = "Add Members"; "lng_profile_delete_and_exit" = "Leave"; -"lng_profile_kick" = "Kick"; -"lng_profile_sure_kick" = "Kick {user} from the group?"; +"lng_profile_kick" = "Remove"; +"lng_profile_sure_kick" = "Remove {user} from the group?"; +"lng_profile_sure_kick_channel" = "Remove {user} from the channel?"; +"lng_profile_sure_kick_admin" = "Remove {user} from administrators?"; "lng_profile_loading" = "Loading.."; "lng_profile_shared_media" = "Shared media"; "lng_profile_no_media" = "No media in this conversation."; @@ -402,13 +408,20 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_profile_audio_files_header" = "Playlist"; "lng_profile_copy_phone" = "Copy phone number"; +"lng_channel_add_admins" = "Add"; +"lng_channel_members" = "Members"; +"lng_channel_admins" = "Administrators"; +"lng_channel_add_admin" = "Add Administrator"; +"lng_channel_admin_sure" = "Add {user} to administrators?"; + "lng_participant_filter" = "Search"; "lng_participant_invite" = "Invite"; +"lng_participant_invite_sorry" = "Sorry, you can only add the first\n{count} members to a channel personally.\n\nFrom now on, people will need\nto join via your invite link."; "lng_create_group_back" = "Back"; "lng_create_group_next" = "Next"; "lng_create_group_create" = "Create"; "lng_create_group_title" = "New Group"; -"lng_create_group_about" = "Groups have up to 200 members and are good for smaller communities"; +"lng_create_group_about" = "Groups have up to {count} members and are good for smaller communities"; "lng_create_channel_title" = "New Channel"; "lng_create_channel_about" = "Channels have unlimited number of members and are good for connecting with large audiences"; "lng_create_public_channel_title" = "Public Channel"; @@ -476,7 +489,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_group_invite_want_join_channel" = "Do you want to join channel «{title}»?"; "lng_group_invite_join" = "Join"; -"lng_group_invite_link" = "Invite link"; +"lng_group_invite_link" = "Invite link:"; "lng_group_invite_create" = "Create an invite link"; "lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link."; "lng_channel_invite_about" = "Telegram users will be able to join\nyour channel by following this link."; @@ -731,6 +744,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_menu_insert_unicode" = "Insert Unicode control character"; +"lng_full_name" = "{first_name} {last_name}"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 6d524b020..0bb12fa88 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1293,7 +1293,7 @@ profileNameFont: font(20px); profileStatusLeft: 22px; profileStatusTop: 31px; profileStatusFont: font(fsize); -profilePhoneLeft: 20px; +profilePhoneLeft: 22px; profilePhoneTop: 62px; profilePhoneFont: font(16px); profileButtonTop: 18px; @@ -1332,6 +1332,8 @@ btnShareContact: flatButton(btnDefNext, btnDefBig) { } profileMinBtnPadding: 10px; +membersPadding: margins(0px, 10px, 0px, 10px); + forwardWidth: 364px; forwardMargins: margins(30px, 10px, 30px, 10px); forwardFont: font(16px); @@ -1462,6 +1464,7 @@ newGroupPhoto: flatButton(btnDefNext, btnDefBig) { newGroupPhotoSize: 96px; newGroupPhotoSkip: 18px; newGroupDescriptionSkip: 28px; +newGroupPublicLinkSkip: 27px; newGroupDescription: flatTextarea(taDefFlat) { font: font(15px); bgColor: transparent; @@ -1832,7 +1835,7 @@ stickerPanSize: size(64px, 64px); stickerPanPadding: 11px; stickerPanDelete: sprite(128px, 132px, 12px, 12px); stickerPanDeleteOpacity: 0.5; -stickerIconPadding: 3px; +stickerIconPadding: 5px; stickerIconOpacity: 0.7; stickerIconSel: 2px; stickerIconSelColor: #58b2ed; @@ -1841,6 +1844,12 @@ stickerIconLeft: sprite(342px, 72px, 40px, 1px); stickerIconRight: sprite(342px, 73px, 40px, 1px); stickerIconMove: 400; +verifiedCheckProfile: sprite(285px, 240px, 22px, 22px); +verifiedCheckProfilePos: point(9px, 4px); +verifiedCheck: sprite(285px, 221px, 19px, 19px); +verifiedCheckInv: sprite(304px, 221px, 19px, 19px); +verifiedCheckPos: point(5px, 0px); + botKbDuration: 200; botKbBg: #f7f7f7; botKbOverBg: #e8ecef; diff --git a/Telegram/SourceFiles/_other/updater.cpp b/Telegram/SourceFiles/_other/updater.cpp index 81afbde5c..69d643864 100644 --- a/Telegram/SourceFiles/_other/updater.cpp +++ b/Telegram/SourceFiles/_other/updater.cpp @@ -252,7 +252,7 @@ bool update() { if (copyResult == FALSE) { writeLog(L"Error: failed to copy, asking to retry.."); WCHAR errMsg[2048]; - wsprintf(errMsg, L"Failed to update Telegram :(\n%s is not accessible.", tofname); + wsprintf(errMsg, L"Failed to update Telegram :(\n%s is not accessible.", tofname.c_str()); if (MessageBox(0, errMsg, L"Update error!", MB_ICONERROR | MB_RETRYCANCEL) != IDRETRY) { delFolder(); return false; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index e5728796e..905639570 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -276,10 +276,11 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result) { } channel->about = qs(f.vabout); channel->count = f.has_participants_count() ? f.vparticipants_count.v : 0; + channel->adminsCount = f.has_admins_count() ? f.vadmins_count.v : 0; channel->invitationUrl = (f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString(); if (History *h = App::historyLoaded(channel->id)) { if (h->inboxReadBefore < f.vread_inbox_max_id.v + 1) { - h->unreadCount = f.vunread_important_count.v; + h->setUnreadCount(f.vunread_important_count.v); h->inboxReadBefore = f.vread_inbox_max_id.v + 1; h->asChannelHistory()->unreadCountAll = f.vunread_count.v; } diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index bfb9e56f0..db3f756b2 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 0404174c0..06e97a19d 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 3d9fda876..4b7b10bdc 100644 --- a/Telegram/SourceFiles/audio.cpp +++ b/Telegram/SourceFiles/audio.cpp @@ -2291,11 +2291,11 @@ public: trySet(_performer, dict, "artist"); trySet(_performer, dict, "performer"); trySet(_performer, dict, "album_artist"); - for (AVDictionaryEntry *tag = av_dict_get(dict, "", 0, AV_DICT_IGNORE_SUFFIX); tag; tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX)) { - const char *key = tag->key; - const char *value = tag->value; - QString tmp = QString::fromUtf8(value); - } + //for (AVDictionaryEntry *tag = av_dict_get(dict, "", 0, AV_DICT_IGNORE_SUFFIX); tag; tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX)) { + // const char *key = tag->key; + // const char *value = tag->value; + // QString tmp = QString::fromUtf8(value); + //} } int64 duration() { diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index efd85d96c..c467a6412 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -20,6 +20,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "application.h" #include "addcontactbox.h" +#include "contactsbox.h" #include "mainwidget.h" #include "window.h" @@ -31,6 +32,7 @@ AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : _firstInput(this, st::inpAddContact, lang(lng_signup_firstname), fname), _lastInput(this, st::inpAddContact, lang(lng_signup_lastname), lname), _phoneInput(this, st::inpAddContact, lang(lng_contact_phone), phone.isEmpty() ? phone : App::formatPhone(phone)), + _invertOrder(langFirstNameGoesSecond()), _contactId(0), _addRequest(0) { if (!phone.isEmpty()) { @@ -48,12 +50,16 @@ AddContactBox::AddContactBox(PeerData *peer) : _firstInput(this, st::inpAddContact, lang(peer->isUser() ? lng_signup_firstname : lng_dlg_new_group_name), peer->isUser() ? peer->asUser()->firstName : peer->name), _lastInput(this, st::inpAddContact, lang(lng_signup_lastname), peer->isUser() ? peer->asUser()->lastName : QString()), _phoneInput(this, st::inpAddContact, lang(lng_contact_phone)), + _invertOrder((!peer || !peer->isChat()) && langFirstNameGoesSecond()), _contactId(0), _addRequest(0) { initBox(); } void AddContactBox::initBox() { + if (_invertOrder) { + setTabOrder(&_lastInput, &_firstInput); + } if (_peer) { if (_peer->isUser()) { _boxTitle = lang(_peer == App::self() ? lng_edit_self_title : lng_edit_contact_title); @@ -61,10 +67,6 @@ void AddContactBox::initBox() { } else if (_peer->isChat()) { _boxTitle = lang(lng_edit_group_title); setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 1 * _firstInput.height() + st::addContactPadding.bottom() + _addButton.height()); - } else if (_peer->isChannel()) { - // CHANNELS_UX - _boxTitle = lang(lng_edit_channel_title); - setMaxHeight(st::boxTitleHeight + st::addContactPadding.top() + 1 * _firstInput.height() + st::addContactPadding.bottom() + _addButton.height()); } } else { bool readyToAdd = !_phoneInput.text().isEmpty() && (!_firstInput.text().isEmpty() || !_lastInput.text().isEmpty()); @@ -107,7 +109,7 @@ void AddContactBox::showAll() { void AddContactBox::showDone() { if ((_firstInput.text().isEmpty() && _lastInput.text().isEmpty()) || _phoneInput.isHidden() || !_phoneInput.isEnabled()) { - _firstInput.setFocus(); + (_invertOrder ? _lastInput : _firstInput).setFocus(); } else { _phoneInput.setFocus(); } @@ -180,9 +182,15 @@ void AddContactBox::paintEvent(QPaintEvent *e) { } void AddContactBox::resizeEvent(QResizeEvent *e) { - _firstInput.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _firstInput.height()); - _lastInput.setGeometry(st::addContactPadding.left(), _firstInput.y() + _firstInput.height() + st::addContactDelta, _firstInput.width(), _firstInput.height()); - _phoneInput.setGeometry(st::addContactPadding.left(), _lastInput.y() + _lastInput.height() + st::addContactDelta, _lastInput.width(), _lastInput.height()); + if (_invertOrder) { + _lastInput.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _lastInput.height()); + _firstInput.setGeometry(st::addContactPadding.left(), _lastInput.y() + _lastInput.height() + st::addContactDelta, _lastInput.width(), _lastInput.height()); + _phoneInput.setGeometry(st::addContactPadding.left(), _firstInput.y() + _firstInput.height() + st::addContactDelta, _lastInput.width(), _lastInput.height()); + } else { + _firstInput.setGeometry(st::addContactPadding.left(), st::boxTitleHeight + st::addContactPadding.top(), width() - st::addContactPadding.left() - st::addContactPadding.right(), _firstInput.height()); + _lastInput.setGeometry(st::addContactPadding.left(), _firstInput.y() + _firstInput.height() + st::addContactDelta, _firstInput.width(), _firstInput.height()); + _phoneInput.setGeometry(st::addContactPadding.left(), _lastInput.y() + _lastInput.height() + st::addContactDelta, _lastInput.width(), _lastInput.height()); + } _cancelButton.move(0, height() - _cancelButton.height()); _addButton.move(width() - _addButton.width(), height() - _addButton.height()); @@ -194,8 +202,13 @@ void AddContactBox::onSend() { QString firstName = _firstInput.text().trimmed(), lastName = _lastInput.text().trimmed(), phone = _phoneInput.text().trimmed(); if (firstName.isEmpty() && lastName.isEmpty()) { - _firstInput.setFocus(); - _firstInput.notaBene(); + if (_invertOrder) { + _lastInput.setFocus(); + _lastInput.notaBene(); + } else { + _firstInput.setFocus(); + _firstInput.notaBene(); + } return; } else if (!_peer && !App::isValidPhone(phone)) { _phoneInput.setFocus(); @@ -212,8 +225,6 @@ void AddContactBox::onSend() { } else if (_peer) { if (_peer->isChat()) { _addRequest = MTP::send(MTPmessages_EditChatTitle(_peer->asChat()->inputChat, MTP_string(firstName)), rpcDone(&AddContactBox::onSaveChatDone), rpcFail(&AddContactBox::onSaveFail)); - } else if (_peer->isChannel()) { - _addRequest = MTP::send(MTPchannels_EditTitle(_peer->asChannel()->inputChannel, MTP_string(firstName)), rpcDone(&AddContactBox::onSaveChatDone), rpcFail(&AddContactBox::onSaveFail)); } else { _contactId = MTP::nonce(); QVector v(1, MTP_inputPhoneContact(MTP_long(_contactId), MTP_string(_peer->asUser()->phone), MTP_string(firstName), MTP_string(lastName))); @@ -345,11 +356,14 @@ a_descriptionBg(st::newGroupName.bgColor->c, st::newGroupName.bgColor->c), a_descriptionBorder(st::newGroupName.borderColor->c, st::newGroupName.borderColor->c), a_description(animFunc(this, &EditChannelBox::descriptionAnimStep)), _description(this, st::newGroupDescription, lang(lng_create_group_description), _channel->about), +_publicLink(this, lang(channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link)), _saveTitleRequestId(0), _saveDescriptionRequestId(0) { _boxTitle = lang(lng_edit_channel_title); _description.installEventFilter(this); + connect(App::main(), SIGNAL(peerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&)), this, SLOT(peerUpdated(PeerData*))); + setMouseTracking(true); _description.resize(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::newGroupDescriptionPadding.left() - st::newGroupDescriptionPadding.right(), _title.height() - st::newGroupDescriptionPadding.top() - st::newGroupDescriptionPadding.bottom()); @@ -365,6 +379,8 @@ _saveTitleRequestId(0), _saveDescriptionRequestId(0) { connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave())); connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onClose())); + connect(&_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink())); + prepare(); } @@ -373,6 +389,7 @@ void EditChannelBox::hideAll() { _description.hide(); _saveButton.hide(); _cancelButton.hide(); + _publicLink.hide(); } void EditChannelBox::showAll() { @@ -380,6 +397,7 @@ void EditChannelBox::showAll() { _description.show(); _saveButton.show(); _cancelButton.show(); + _publicLink.show(); } void EditChannelBox::showDone() { @@ -439,6 +457,12 @@ bool EditChannelBox::descriptionAnimStep(float64 ms) { return res; } +void EditChannelBox::peerUpdated(PeerData *peer) { + if (peer == _channel) { + _publicLink.setText(lang(_channel->isPublic() ? lng_profile_edit_public_link : lng_profile_create_public_link)); + } +} + void EditChannelBox::onDescriptionResized() { updateMaxHeight(); update(); @@ -449,8 +473,10 @@ QRect EditChannelBox::descriptionRect() const { } void EditChannelBox::updateMaxHeight() { - int32 h = st::boxTitleHeight + st::newGroupPadding.top() + _title.height() + st::newGroupPadding.bottom() + _saveButton.height(); + int32 h = st::boxTitleHeight + st::newGroupPadding.top() + _title.height(); h += st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top() + _description.height() + st::newGroupDescriptionPadding.bottom(); + h += st::newGroupPublicLinkSkip + _publicLink.height(); + h += st::newGroupPadding.bottom() + _saveButton.height(); setMaxHeight(h); } @@ -475,8 +501,9 @@ void EditChannelBox::resizeEvent(QResizeEvent *e) { _description.moveToLeft(st::newGroupPadding.left() + st::newGroupDescriptionPadding.left(), _title.y() + _title.height() + st::newGroupDescriptionSkip + st::newGroupDescriptionPadding.top(), width()); - int32 buttonTop = _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom(); - buttonTop += st::newGroupPadding.bottom(); + _publicLink.moveToLeft(st::newGroupPadding.left(), _description.y() + _description.height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkSkip, width()); + + int32 buttonTop = _publicLink.y() + _publicLink.height() + st::newGroupPadding.bottom(); _cancelButton.move(0, buttonTop); _saveButton.move(width() - _saveButton.width(), buttonTop); } @@ -521,6 +548,10 @@ void EditChannelBox::onSave() { _saveTitleRequestId = MTP::send(MTPchannels_EditTitle(_channel->inputChannel, MTP_string(_sentTitle)), rpcDone(&EditChannelBox::onSaveTitleDone), rpcFail(&EditChannelBox::onSaveFail)); } +void EditChannelBox::onPublicLink() { + App::wnd()->replaceLayer(new SetupChannelBox(_channel, true)); +} + void EditChannelBox::saveDescription() { _saveDescriptionRequestId = MTP::send(MTPchannels_EditAbout(_channel->inputChannel, MTP_string(_sentDescription)), rpcDone(&EditChannelBox::onSaveDescriptionDone), rpcFail(&EditChannelBox::onSaveFail)); } diff --git a/Telegram/SourceFiles/boxes/addcontactbox.h b/Telegram/SourceFiles/boxes/addcontactbox.h index ad1cfb33a..b7c20ec58 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.h +++ b/Telegram/SourceFiles/boxes/addcontactbox.h @@ -64,6 +64,8 @@ private: FlatButton _addButton, _retryButton, _cancelButton; FlatInput _firstInput, _lastInput, _phoneInput; + bool _invertOrder; + uint64 _contactId; mtpRequestId _addRequest; @@ -95,8 +97,11 @@ public: public slots: + void peerUpdated(PeerData *peer); + void onSave(); void onDescriptionResized(); + void onPublicLink(); protected: @@ -127,6 +132,8 @@ private: Animation a_description; FlatTextarea _description; + LinkButton _publicLink; + mtpRequestId _saveTitleRequestId, _saveDescriptionRequestId; QString _sentTitle, _sentDescription; }; diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index 45e4cd28f..407b09929 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -22,6 +22,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "window.h" +#include "application.h" + TextParseOptions _confirmBoxTextOptions = { TextParseLinks | TextParseMultiline | TextParseRichText, // flags 0, // maxw @@ -195,3 +197,97 @@ void ConfirmLinkBox::onOpenLink() { } App::wnd()->hideLayer(); } + +MaxInviteBox::MaxInviteBox(const QString &link) : +_close(this, lang(lng_close), st::btnInfoClose), +_text(st::boxFont, lng_participant_invite_sorry(lt_count, QString::number(cMaxGroupCount())), _confirmBoxTextOptions), +_link(link), _linkOver(false), +a_goodOpacity(0, 0), a_good(animFunc(this, &MaxInviteBox::goodAnimStep)) { + setMouseTracking(true); + + _textWidth = st::boxWidth + 10 - st::boxPadding.left() - st::boxPadding.right(); + _textHeight = qMin(_text.countHeight(_textWidth), 16 * st::boxFont->height); + setMaxHeight(st::boxPadding.top() + _textHeight + st::newGroupLinkPadding.top() + st::newGroupLink.height + st::newGroupLinkPadding.bottom() + _close.height()); + + connect(&_close, SIGNAL(clicked()), this, SLOT(onClose())); + + prepare(); +} + +void MaxInviteBox::mouseMoveEvent(QMouseEvent *e) { + updateSelected(e->globalPos()); +} + +void MaxInviteBox::mousePressEvent(QMouseEvent *e) { + mouseMoveEvent(e); + if (_linkOver) { + App::app()->clipboard()->setText(_link); + _goodTextLink = lang(lng_create_channel_link_copied); + a_goodOpacity = anim::fvalue(1, 0); + a_good.start(); + } +} + +void MaxInviteBox::leaveEvent(QEvent *e) { + updateSelected(QCursor::pos()); +} + +void MaxInviteBox::updateSelected(const QPoint &cursorGlobalPosition) { + QPoint p(mapFromGlobal(cursorGlobalPosition)); + + bool linkOver = _invitationLink.contains(p); + if (linkOver != _linkOver) { + _linkOver = linkOver; + update(); + setCursor(_linkOver ? style::cur_pointer : style::cur_default); + } +} + +bool MaxInviteBox::goodAnimStep(float64 ms) { + float dt = ms / st::newGroupLinkFadeDuration; + bool res = true; + if (dt >= 1) { + res = false; + a_goodOpacity.finish(); + } else { + a_goodOpacity.update(dt, anim::linear); + } + update(); + return res; +} + +void MaxInviteBox::hideAll() { + _close.hide(); +} + +void MaxInviteBox::showAll() { + _close.show(); +} + +void MaxInviteBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + // draw box title / text + p.setFont(st::boxFont->f); + p.setPen(st::black->p); + _text.drawElided(p, st::boxPadding.left(), st::boxPadding.top(), _textWidth, 16, (_text.maxWidth() < width()) ? style::al_center : style::al_left); + + QTextOption option(style::al_left); + option.setWrapMode(QTextOption::WrapAnywhere); + p.setFont(_linkOver ? st::newGroupLink.font->underline() : st::newGroupLink.font); + p.setPen(st::btnDefLink.color); + p.drawText(_invitationLink, _link, option); + if (!_goodTextLink.isEmpty() && a_goodOpacity.current() > 0) { + p.setOpacity(a_goodOpacity.current()); + p.setPen(st::setGoodColor->p); + p.setFont(st::setErrFont->f); + p.drawText(QRect(st::newGroupPadding.left(), st::boxPadding.top() + _textHeight + st::newGroupLinkTop + st::newGroupLinkFont->height - st::setErrFont->ascent, width() - st::newGroupPadding.left() - st::newGroupPadding.right(), st::setErrFont->height), _goodTextLink, style::al_top); + p.setOpacity(1); + } +} + +void MaxInviteBox::resizeEvent(QResizeEvent *e) { + _close.move(0, height() - _close.height()); + _invitationLink = QRect(st::newGroupPadding.left(), st::boxPadding.top() + _textHeight + st::newGroupLinkPadding.top() + (st::newGroupLink.height / 2) - st::newGroupLinkFont->height, width() - st::newGroupPadding.left() - st::newGroupPadding.right(), 2 * st::newGroupLinkFont->height); +} diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index 101e27d3c..c6f1e7b5e 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -19,7 +19,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "abstractbox.h" -class ConfirmBox : public AbstractBox, public RPCSender { +class ConfirmBox : public AbstractBox { Q_OBJECT public: @@ -84,3 +84,41 @@ private: QString _url; }; + +class MaxInviteBox : public AbstractBox { + Q_OBJECT + +public: + + MaxInviteBox(const QString &link); + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mousePressEvent(QMouseEvent *e); + void leaveEvent(QEvent *e); + void updateLink(); + +protected: + + void hideAll(); + void showAll(); + +private: + + void updateSelected(const QPoint &cursorGlobalPosition); + bool goodAnimStep(float64 ms); + + BottomButton _close; + Text _text; + int32 _textWidth, _textHeight; + + QString _link; + QRect _invitationLink; + bool _linkOver; + + QPoint _lastMousePos; + + QString _goodTextLink; + anim::fvalue a_goodOpacity; + Animation a_good; +}; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 8ce6d1174..c62fe8371 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -30,7 +30,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "confirmbox.h" -ContactsInner::ContactsInner(CreatingGroupType creating) : _chat(0), _channel(0), _bot(0), _creating(creating), _addToChat(0), +ContactsInner::ContactsInner(CreatingGroupType creating) : +_chat(0), _channel(0), _channelFilter(MembersFilterRecent), _bot(0), _creating(creating), +_addToChat(0), _addAdmin(0), _addAdminRequestId(0), _addAdminBox(0), _contacts(&App::main()->contactsList()), _sel(0), _filteredSel(-1), @@ -42,7 +44,9 @@ _addContactLnk(this, lang(lng_add_contact_button)) { init(); } -ContactsInner::ContactsInner(ChannelData *channel) : _chat(0), _channel(channel), _bot(0), _creating(CreatingGroupChannel), _addToChat(0), +ContactsInner::ContactsInner(ChannelData *channel, MembersFilter channelFilter, const MembersAlreadyIn &already) : +_chat(0), _channel(channel), _channelFilter(channelFilter), _bot(0), _creating(CreatingGroupChannel), _already(already), +_addToChat(0), _addAdmin(0), _addAdminRequestId(0), _addAdminBox(0), _contacts(&App::main()->contactsList()), _sel(0), _filteredSel(-1), @@ -54,7 +58,9 @@ _addContactLnk(this, lang(lng_add_contact_button)) { init(); } -ContactsInner::ContactsInner(ChatData *chat) : _chat(chat), _channel(0), _bot(0), _creating(CreatingGroupNone), _addToChat(0), +ContactsInner::ContactsInner(ChatData *chat) : +_chat(chat), _channel(0), _channelFilter(MembersFilterRecent), _bot(0), _creating(CreatingGroupNone), +_addToChat(0), _addAdmin(0), _addAdminRequestId(0), _addAdminBox(0), _contacts(&App::main()->contactsList()), _sel(0), _filteredSel(-1), @@ -66,7 +72,9 @@ _addContactLnk(this, lang(lng_add_contact_button)) { init(); } -ContactsInner::ContactsInner(UserData *bot) : _chat(0), _channel(0), _bot(bot), _creating(CreatingGroupNone), _addToChat(0), +ContactsInner::ContactsInner(UserData *bot) : +_chat(0), _channel(0), _channelFilter(MembersFilterRecent), _bot(bot), _creating(CreatingGroupNone), +_addToChat(0), _addAdmin(0), _addAdminRequestId(0), _addAdminBox(0), _contacts(new DialogsIndexed(DialogsSortByAdd)), _sel(0), _filteredSel(-1), @@ -117,6 +125,36 @@ void ContactsInner::onAddBot() { App::main()->showPeerHistory(_addToChat->id, ShowAtUnreadMsgId); } +void ContactsInner::onAddAdmin() { + if (_addAdminRequestId) return; + _addAdminRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _addAdmin->inputUser, MTP_channelRoleEditor()), rpcDone(&ContactsInner::addAdminDone), rpcFail(&ContactsInner::addAdminFail)); +} + +void ContactsInner::onNoAddAdminBox(QObject *obj) { + if (obj == _addAdminBox) { + _addAdminBox = 0; + } +} + +void ContactsInner::addAdminDone(const MTPBool &result, mtpRequestId req) { + if (req != _addAdminRequestId) return; + + _addAdminRequestId = 0; + if (_addAdminBox) _addAdminBox->onClose(); + emit adminAdded(); +} + +bool ContactsInner::addAdminFail(const RPCError &error, mtpRequestId req) { + if (mtpIsFlood(error)) return false; + + if (req != _addAdminRequestId) return true; + + _addAdminRequestId = 0; + if (_addAdminBox) _addAdminBox->onClose(); + emit adminAdded(); + return true; +} + void ContactsInner::peerUpdated(PeerData *peer) { if (_chat && (!peer || peer == _chat)) { if (_chat->isForbidden || _chat->haveLeft) { @@ -197,8 +235,10 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) { if (peer->isUser()) { if (_chat) { data->inchat = _chat->participants.contains(peer->asUser()); - } else if (_creating == CreatingGroupGroup || _channel) { + } else if (_creating == CreatingGroupGroup) { data->inchat = (peerToUser(peer->id) == MTP::authedId()); + } else if (_channel) { + data->inchat = (peerToUser(peer->id) == MTP::authedId()) || _already.contains(peer->asUser()); } else { data->inchat = false; } @@ -227,7 +267,7 @@ ContactsInner::ContactData *ContactsInner::contactData(DialogRow *row) { return data; } -void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data, bool sel) { +void ContactsInner::paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel) { int32 left = st::profileListPadding.width(); UserData *user = peer->asUser(); @@ -248,9 +288,18 @@ void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data, p.setPen(st::profileListNameColor->p); } int32 iconw = (_chat || _creating != CreatingGroupNone) ? st::profileCheckRect.pxWidth() : st::contactsImg.pxWidth(); - data->name.drawElided(p, left + st::profileListPhotoSize + st::participantDelta, st::profileListNameTop, width() - left - st::profileListPhotoSize - st::profileListPadding.width() - st::participantDelta - st::scrollDef.width - iconw); + int32 namew = width() - left - st::profileListPhotoSize - st::profileListPadding.width() - st::participantDelta - st::scrollDef.width - iconw; + if (peer->isChannel() && peer->asChannel()->isVerified()) { + namew -= st::verifiedCheck.pxWidth() + st::verifiedCheckPos.x(); + p.drawSprite(QPoint(left + st::profileListPhotoSize + st::participantDelta + qMin(data->name.maxWidth(), namew), st::profileListNameTop) + st::verifiedCheckPos, st::verifiedCheck); + } + data->name.drawElided(p, left + st::profileListPhotoSize + st::participantDelta, st::profileListNameTop, namew); - if (_chat || _creating !=CreatingGroupNone) { + if (_channel && _channelFilter == MembersFilterAdmins) { + if (sel) { + p.drawPixmap(QPoint(width() - st::contactsImg.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::contactsImg.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), st::contactsImg); + } + } else if (_chat || _creating != CreatingGroupNone) { if (sel || data->check) { p.drawPixmap(QPoint(width() - st::profileCheckRect.pxWidth() - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::profileCheckRect.pxHeight()) / 2 - st::profileCheckDeltaY), App::sprite(), (data->check ? st::profileCheckActiveRect : st::profileCheckRect)); } @@ -287,7 +336,7 @@ void ContactsInner::paintDialog(QPainter &p, PeerData *peer, ContactData *data, void ContactsInner::paintEvent(QPaintEvent *e) { QRect r(e->rect()); - QPainter p(this); + Painter p(this); _time = unixtime(); p.fillRect(r, st::white->b); @@ -422,7 +471,8 @@ void ContactsInner::mousePressEvent(QMouseEvent *e) { } void ContactsInner::chooseParticipant() { - if (_chat || _creating != CreatingGroupNone) { + bool addingAdmin = (_channel && _channelFilter == MembersFilterAdmins); + if (!addingAdmin && (_chat || _creating != CreatingGroupNone)) { _time = unixtime(); int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from; if (_filter.isEmpty()) { @@ -481,7 +531,18 @@ void ContactsInner::chooseParticipant() { } } if (peer) { - if (bot() && peer->isChat()) { + if (addingAdmin) { + _addAdmin = peer->asUser(); + if (_addAdminRequestId) { + MTP::cancel(_addAdminRequestId); + _addAdminRequestId = 0; + } + if (_addAdminBox) _addAdminBox->deleteLater(); + _addAdminBox = new ConfirmBox(lng_channel_admin_sure(lt_user, _addAdmin->firstName)); + connect(_addAdminBox, SIGNAL(confirmed()), this, SLOT(onAddAdmin())); + connect(_addAdminBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoAddAdminBox(QObject*))); + App::wnd()->replaceLayer(_addAdminBox); + } else if (bot() && peer->isChat()) { _addToChat = peer->asChat(); ConfirmBox *box = new ConfirmBox(lng_bot_sure_invite(lt_group, peer->name)); connect(box, SIGNAL(confirmed()), this, SLOT(onAddBot())); @@ -519,7 +580,7 @@ int32 ContactsInner::selectedCount() const { if (_chat) { result += (_chat->count > 0) ? _chat->count : 1; } else if (_channel) { - result += (_channel->count > 0) ? _channel->count : 1; + result += _already.size(); } else if (_creating == CreatingGroupGroup) { result += 1; } @@ -785,6 +846,10 @@ ChannelData *ContactsInner::channel() const { return _channel; } +MembersFilter ContactsInner::channelFilter() const { + return _channelFilter; +} + UserData *ContactsInner::bot() const { return _bot; } @@ -1017,6 +1082,15 @@ _creationRequestId(0) { init(); } +ContactsBox::ContactsBox(ChannelData *channel, MembersFilter filter, const MembersAlreadyIn &already) : ItemListBox(st::boxNoTopScroll), _inner(channel, filter, already), +_addContact(this, lang(lng_add_contact_button), st::contactsAdd), +_filter(this, st::contactsFilter, lang(lng_participant_filter)), +_next(this, lang(lng_participant_invite), st::btnSelectDone), +_cancel(this, lang(filter == MembersFilterAdmins ? lng_contacts_done : lng_cancel), (filter == MembersFilterAdmins ? st::contactsClose : st::btnSelectCancel)), +_creationRequestId(0) { + init(); +} + ContactsBox::ContactsBox(ChatData *chat) : ItemListBox(st::boxNoTopScroll), _inner(chat), _addContact(this, lang(lng_add_contact_button), st::contactsAdd), _filter(this, st::contactsFilter, lang(lng_participant_filter)), @@ -1060,6 +1134,7 @@ void ContactsBox::init() { connect(&_inner, SIGNAL(mustScrollTo(int, int)), &_scroll, SLOT(scrollToY(int, int))); connect(&_inner, SIGNAL(selectAllQuery()), &_filter, SLOT(selectAll())); connect(&_inner, SIGNAL(searchByUsername()), this, SLOT(onNeedSearchByUsername())); + connect(&_inner, SIGNAL(adminAdded()), this, SIGNAL(adminAdded())); _searchTimer.setSingleShot(true); connect(&_searchTimer, SIGNAL(timeout()), this, SLOT(onSearchByUsername())); @@ -1146,7 +1221,10 @@ void ContactsBox::hideAll() { void ContactsBox::showAll() { ItemListBox::showAll(); _filter.show(); - if (_inner.chat()) { + if (_inner.channel() && _inner.channelFilter() == MembersFilterAdmins) { + _next.hide(); + _addContact.hide(); + } else if (_inner.chat()) { _next.show(); _addContact.hide(); } else if (_inner.creating() != CreatingGroupNone) { @@ -1195,15 +1273,18 @@ void ContactsBox::paintEvent(QPaintEvent *e) { Painter p(this); if (paint(p)) return; + bool addingAdmin = _inner.channel() && _inner.channelFilter() == MembersFilterAdmins; if (_inner.chat() || _inner.creating() != CreatingGroupNone) { - QString title(lang(lng_profile_add_participant)); + QString title(lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant)); paintTitle(p, title, true); - p.setPen(st::newGroupLimitFg); - p.drawTextLeft(st::boxTitlePos.x() + st::boxTitleFont->m.width(title) + st::addContactDelta, st::boxTitlePos.y(), width(), QString("%1 / %2").arg(_inner.selectedCount()).arg(cMaxGroupCount())); + if (!addingAdmin) { + p.setPen(st::newGroupLimitFg); + p.drawTextLeft(st::boxTitlePos.x() + st::boxTitleFont->m.width(title) + st::addContactDelta, st::boxTitlePos.y(), width(), QString("%1 / %2").arg(_inner.selectedCount()).arg(cMaxGroupCount())); - // paint button sep - p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b); + // paint button sep + p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b); + } } else if (_inner.bot()) { paintTitle(p, lang(lng_bot_choose_group), true); } else { @@ -1221,7 +1302,7 @@ void ContactsBox::resizeEvent(QResizeEvent *e) { } void ContactsBox::closePressed() { - if (_inner.channel()) { + if (_inner.channel() && !_inner.hasAlreadyMembersInChannel()) { App::main()->showPeerHistory(_inner.channel()->id, ShowAtTheEndMsgId); } } @@ -1335,11 +1416,551 @@ bool ContactsBox::creationFail(const RPCError &error) { return false; } +MembersInner::MembersInner(ChannelData *channel, MembersFilter filter) : _channel(channel), _filter(filter), +_time(0), +_kickText(lang(lng_profile_kick)), +_kickWidth(st::normalFont->m.width(_kickText)), +_sel(-1), +_kickSel(-1), +_kickDown(-1), +_mouseSel(false), +_kickConfirm(0), +_kickRequestId(0), +_kickBox(0), +_loading(true), +_loadingRequestId(0) { + connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&))); + connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); + + refresh(); + + load(); +} + +void MembersInner::load() { + if (!_loadingRequestId) { + _loadingRequestId = MTP::send(MTPchannels_GetParticipants(_channel->inputChannel, (_filter == MembersFilterRecent) ? MTP_channelParticipantsRecent() : MTP_channelParticipantsAdmins(), MTP_int(0), MTP_int(cMaxGroupCount())), rpcDone(&MembersInner::membersReceived), rpcFail(&MembersInner::membersFailed)); + } +} + +void MembersInner::paintEvent(QPaintEvent *e) { + QRect r(e->rect()); + Painter p(this); + + _time = unixtime(); + p.fillRect(r, st::white->b); + + int32 yFrom = r.top(), yTo = r.bottom(); + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + + p.translate(0, st::membersPadding.top()); + if (_rows.isEmpty()) { + p.setFont(st::noContactsFont->f); + p.setPen(st::noContactsColor->p); + p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); + } else { + int32 from = (yFrom >= 0) ? (yFrom / rh) : 0; + if (from < _rows.size()) { + int32 to = (yTo / rh) + 1; + if (to > _rows.size()) to = _rows.size(); + + p.translate(0, from * rh); + for (; from < to; ++from) { + bool sel = (from == _sel); + bool kickSel = (from == _kickSel && (_kickDown < 0 || from == _kickDown)); + bool kickDown = kickSel && (from == _kickDown); + paintDialog(p, _rows[from], data(from), sel, kickSel, kickDown); + p.translate(0, rh); + } + } + } +} + +void MembersInner::enterEvent(QEvent *e) { + setMouseTracking(true); +} + +void MembersInner::leaveEvent(QEvent *e) { + setMouseTracking(false); + if (_sel >= 0) { + _sel = -1; + parentWidget()->update(); + } +} + +void MembersInner::mouseMoveEvent(QMouseEvent *e) { + _mouseSel = true; + _lastMousePos = e->globalPos(); + updateSel(); +} + +void MembersInner::mousePressEvent(QMouseEvent *e) { + _mouseSel = true; + _lastMousePos = e->globalPos(); + updateSel(); + if (e->button() == Qt::LeftButton && _kickSel < 0) { + chooseParticipant(); + } + _kickDown = _kickSel; + update(); +} + +void MembersInner::mouseReleaseEvent(QMouseEvent *e) { + _mouseSel = true; + _lastMousePos = e->globalPos(); + updateSel(); + if (_kickDown >= 0 && _kickDown == _kickSel && !_kickRequestId) { + _kickConfirm = _rows.at(_kickSel); + if (_kickBox) _kickBox->deleteLater(); + _kickBox = new ConfirmBox((_filter == MembersFilterRecent ? lng_profile_sure_kick_channel : lng_profile_sure_kick_admin)(lt_user, _kickConfirm->firstName)); + connect(_kickBox, SIGNAL(confirmed()), this, SLOT(onKickConfirm())); + connect(_kickBox, SIGNAL(destroyed(QObject*)), this, SLOT(onKickBoxDestroyed(QObject*))); + App::wnd()->replaceLayer(_kickBox); + } + _kickDown = -1; +} + +void MembersInner::onKickBoxDestroyed(QObject *obj) { + if (_kickBox == obj) { + _kickBox = 0; + } +} + +void MembersInner::onKickConfirm() { + if (_filter == MembersFilterRecent) { + _kickRequestId = MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _kickConfirm->inputUser, MTP_bool(true)), rpcDone(&MembersInner::kickDone), rpcFail(&MembersInner::kickFail)); + } else { + _kickRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _kickConfirm->inputUser, MTP_channelRoleEmpty()), rpcDone(&MembersInner::kickAdminDone), rpcFail(&MembersInner::kickFail)); + } +} + +void MembersInner::paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown) { + int32 left = st::profileListPadding.width(); + + UserData *user = peer->asUser(); + if (sel) { + p.fillRect(0, 0, width(), 2 * st::profileListPadding.height() + st::profileListPhotoSize, st::profileHoverBG->b); + } + + p.drawPixmap(left, st::profileListPadding.height(), peer->photo->pix(st::profileListPhotoSize)); + + p.setPen(st::profileListNameColor->p); + + data->name.drawElided(p, left + st::profileListPhotoSize + st::participantDelta, st::profileListNameTop, width() - left - st::profileListPhotoSize - st::profileListPadding.width() - st::participantDelta - st::scrollDef.width - (data->canKick ? _kickWidth : 0)); + + if (data->canKick) { + p.setFont((kickSel ? st::linkOverFont : st::linkFont)->f); + if (kickDown) { + p.setPen(st::btnDefLink.downColor->p); + } else { + p.setPen(st::btnDefLink.color->p); + } + p.drawText(width() - _kickWidth - st::profileCheckDeltaX, st::profileListPadding.height() + (st::profileListPhotoSize - st::normalFont->height) / 2 + st::normalFont->ascent, _kickText); + } + + p.setFont(st::normalFont); + p.setPen(st::profileOfflineColor->p); + p.drawText(left + st::profileListPhotoSize + st::profileListPadding.width(), st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online); +} + +void MembersInner::selectSkip(int32 dir) { + _time = unixtime(); + _mouseSel = false; + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, origDir = dir; + + int cur = (_sel >= 0) ? _sel : -1; + cur += dir; + if (cur <= 0) { + _sel = _rows.isEmpty() ? -1 : 0; + } else if (cur >= _rows.size()) { + _sel = -1; + } else { + _sel = cur; + } + if (dir > 0) { + if (_sel < 0 || _sel >= _rows.size()) { + _sel = -1; + } + } else { + if (!_rows.isEmpty()) { + if (_sel < 0) _sel = _rows.size() - 1; + } + } + if (_sel >= 0) { + emit mustScrollTo(_sel * rh, (_sel + 1) * rh); + } + + parentWidget()->update(); +} + +void MembersInner::selectSkipPage(int32 h, int32 dir) { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + int32 points = h / rh; + if (!points) return; + selectSkip(points * dir); +} + +void MembersInner::loadProfilePhotos(int32 yFrom) { + int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; + MTP::clearLoaderPriorities(); + + if (yTo < 0) return; + if (yFrom < 0) yFrom = 0; + + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + if (!_rows.isEmpty()) { + int32 from = yFrom / rh; + if (from < 0) from = 0; + if (from < _rows.size()) { + int32 to = (yTo / rh) + 1; + if (to > _rows.size()) to = _rows.size(); + + for (; from < to; ++from) { + _rows[from]->photo->load(); + } + } + } +} + +void MembersInner::chooseParticipant() { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2, from; + if (_sel < 0 || _sel >= _rows.size()) return; + if (PeerData *peer = _rows[_sel]) { + App::wnd()->hideLayer(); + App::main()->showPeerProfile(peer, ShowAtUnreadMsgId); + } +} + +void MembersInner::refresh() { + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + if (_rows.isEmpty()) { + resize(width(), st::membersPadding.top() + st::noContactsHeight + st::membersPadding.bottom()); + } else { + resize(width(), st::membersPadding.top() + _rows.size() * rh + st::membersPadding.bottom()); + } + update(); +} + +ChannelData *MembersInner::channel() const { + return _channel; +} + +MembersFilter MembersInner::filter() const { + return _filter; +} + +QMap 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); + } + } + return result; +} + +void MembersInner::clearSel() { + _sel = _kickSel = _kickDown = -1; + _lastMousePos = QCursor::pos(); + updateSel(); +} + +MembersInner::MemberData *MembersInner::data(int32 index) { + if (MemberData *result = _datas.at(index)) { + return result; + } + MemberData *result = _datas[index] = new MemberData(); + result->name.setText(st::profileListNameFont, _rows[index]->name, _textNameOptions); + result->online = lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); + if (_filter == MembersFilterRecent) { + result->canKick = (_channel->amCreator() || _channel->amEditor() || _channel->amModerator()) ? (_roles[index] == MemberRoleNone) : false; + } else if (_filter == MembersFilterAdmins) { + result->canKick = _channel->amCreator() ? (_roles[index] == MemberRoleEditor || _roles[index] == MemberRoleModerator) : false; + } else { + result->canKick = false; + } + return result; +} + +void MembersInner::clear() { + for (int32 i = 0, l = _datas.size(); i < l; ++i) { + delete _datas.at(i); + } + _datas.clear(); + _rows.clear(); + _dates.clear(); + _roles.clear(); + if (_kickBox) _kickBox->deleteLater(); + clearSel(); +} + +MembersInner::~MembersInner() { + clear(); +} + +void MembersInner::updateSel() { + QPoint p(mapFromGlobal(_lastMousePos)); + p.setY(p.y() - st::membersPadding.top()); + bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); + int32 rh = st::profileListPhotoSize + st::profileListPadding.height() * 2; + int32 newSel = (in && p.y() >= 0 && p.y() < _rows.size() * rh) ? (p.y() / rh) : -1; + int32 newKickSel = newSel; + if (newSel >= 0 && (!data(newSel)->canKick || !QRect(width() - _kickWidth - st::profileCheckDeltaX, newSel * rh + st::profileListPadding.height() + (st::profileListPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { + newKickSel = -1; + } + if (newSel != _sel || newKickSel != _kickSel) { + _sel = newSel; + _kickSel = newKickSel; + parentWidget()->update(); + setCursor(_kickSel >= 0 ? style::cur_pointer : style::cur_default); + } +} + +void MembersInner::peerUpdated(PeerData *peer) { + parentWidget()->update(); +} + +void MembersInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { + for (int32 i = 0, l = _rows.size(); i < l; ++i) { + if (_rows.at(i) == peer) { + if (_datas.at(i)) { + _datas.at(i)->name.setText(st::profileListNameFont, peer->name, _textNameOptions); + parentWidget()->update(); + } else { + break; + } + } + } +} + +void MembersInner::membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req) { + clear(); + _loadingRequestId = 0; + + if (result.type() == mtpc_channels_channelParticipants) { + const MTPDchannels_channelParticipants &d(result.c_channels_channelParticipants()); + const QVector &v(d.vparticipants.c_vector().v); + _rows.reserve(v.size()); + _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 (App::main()) emit App::main()->peerUpdated(_channel); + } else if (_filter == MembersFilterAdmins && _channel->adminsCount != d.vcount.v) { + _channel->adminsCount = d.vcount.v; + if (App::main()) emit App::main()->peerUpdated(_channel); + } + App::feedUsers(d.vusers); + for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { + int32 userId = 0, addedTime = 0; + MemberRole role = MemberRoleNone; + switch (i->type()) { + case mtpc_channelParticipant: + userId = i->c_channelParticipant().vuser_id.v; + addedTime = i->c_channelParticipant().vdate.v; + break; + case mtpc_channelParticipantSelf: + role = MemberRoleSelf; + userId = i->c_channelParticipantSelf().vuser_id.v; + addedTime = i->c_channelParticipantSelf().vdate.v; + break; + case mtpc_channelParticipantModerator: + role = MemberRoleModerator; + userId = i->c_channelParticipantModerator().vuser_id.v; + addedTime = i->c_channelParticipantModerator().vdate.v; + break; + case mtpc_channelParticipantEditor: + role = MemberRoleEditor; + userId = i->c_channelParticipantEditor().vuser_id.v; + addedTime = i->c_channelParticipantEditor().vdate.v; + break; + case mtpc_channelParticipantKicked: + userId = i->c_channelParticipantKicked().vuser_id.v; + addedTime = i->c_channelParticipantKicked().vdate.v; + role = MemberRoleKicked; + break; + case mtpc_channelParticipantCreator: + userId = i->c_channelParticipantCreator().vuser_id.v; + addedTime = _channel->date; + role = MemberRoleCreator; + break; + } + if (UserData *user = App::userLoaded(userId)) { + _rows.push_back(user); + _dates.push_back(date(addedTime)); + _roles.push_back(role); + _datas.push_back(0); + } + } + } + if (_rows.isEmpty()) { + _rows.push_back(App::self()); + _dates.push_back(date(MTP_int(_channel->date))); + _roles.push_back(MemberRoleSelf); + _datas.push_back(0); + } + + clearSel(); + _loading = false; + refresh(); + + emit loaded(); +} + +bool MembersInner::membersFailed(const RPCError &error, mtpRequestId req) { + if (mtpIsFlood(error)) return false; + App::wnd()->hideLayer(); + return true; +} + +void MembersInner::kickDone(const MTPUpdates &result, mtpRequestId req) { + App::main()->sentUpdatesReceived(result); + + if (_kickRequestId != req) return; + removeKicked(); + if (_kickBox) _kickBox->onClose(); +} + +void MembersInner::kickAdminDone(const MTPBool &result, mtpRequestId req) { + if (_kickRequestId != req) return; + removeKicked(); + if (_kickBox) _kickBox->onClose(); +} + +bool MembersInner::kickFail(const RPCError &error, mtpRequestId req) { + if (mtpIsFlood(error)) return false; + + if (_kickBox) _kickBox->onClose(); + load(); + return true; +} + +void MembersInner::removeKicked() { + _kickRequestId = 0; + int32 index = _rows.indexOf(_kickConfirm); + if (index >= 0) { + _rows.removeAt(index); + delete _datas.at(index); + _datas.removeAt(index); + _dates.removeAt(index); + _roles.removeAt(index); + clearSel(); + if (_filter == MembersFilterRecent && _channel->count > 1) { + --_channel->count; + if (App::main()) emit App::main()->peerUpdated(_channel); + } else if (_filter == MembersFilterAdmins && _channel->adminsCount > 1) { + --_channel->adminsCount; + if (App::main()) emit App::main()->peerUpdated(_channel); + } + } + _kickConfirm = 0; +} + +MembersBox::MembersBox(ChannelData *channel, MembersFilter filter) : ItemListBox(st::boxScroll), _inner(channel, filter), +_add(this, lang(filter == MembersFilterRecent ? lng_participant_invite : lng_channel_add_admins), st::contactsAdd), +_done(this, lang(lng_contacts_done), st::contactsClose), +_addBox(0) { + ItemListBox::init(&_inner, _done.height()); + + connect(&_add, SIGNAL(clicked()), this, SLOT(onAdd())); + + connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); + connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSel())); + connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); + connect(&_inner, SIGNAL(mustScrollTo(int, int)), &_scroll, SLOT(scrollToY(int, int))); + connect(&_inner, SIGNAL(loaded()), this, SLOT(onLoaded())); + + prepare(); +} + +void MembersBox::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Down) { + _inner.selectSkip(1); + } else if (e->key() == Qt::Key_Up) { + _inner.selectSkip(-1); + } else if (e->key() == Qt::Key_PageDown) { + _inner.selectSkipPage(_scroll.height(), 1); + } else if (e->key() == Qt::Key_PageUp) { + _inner.selectSkipPage(_scroll.height(), -1); + } else { + ItemListBox::keyPressEvent(e); + } +} + +void MembersBox::paintEvent(QPaintEvent *e) { + Painter p(this); + if (paint(p)) return; + + QString title(lang(_inner.filter() == MembersFilterRecent ? lng_channel_members : lng_channel_admins)); + paintTitle(p, title, false); +} + +void MembersBox::resizeEvent(QResizeEvent *e) { + ItemListBox::resizeEvent(e); + _inner.resize(width(), _inner.height()); + _done.move(0, height() - _done.height()); + _add.move(width() - _add.width(), 0); +} + +void MembersBox::onLoaded() { + if (!_done.isHidden() && _inner.channel()->amCreator() && (_inner.channel()->count < cMaxGroupCount() || !_inner.channel()->isPublic())) { + _add.show(); + } +} + +void MembersBox::onScroll() { + _inner.loadProfilePhotos(_scroll.scrollTop()); +} + +void MembersBox::onAdd() { + if (_inner.filter() == MembersFilterRecent && _inner.channel()->count >= cMaxGroupCount()) { + App::wnd()->replaceLayer(new MaxInviteBox(_inner.channel()->invitationUrl)); + return; + } + ContactsBox *box = new ContactsBox(_inner.channel(), _inner.filter(), _inner.already()); + if (_inner.filter() == MembersFilterRecent) { + App::wnd()->hideLayer(true); + App::wnd()->showLayer(box, true); + } else { + _addBox = box; + connect(_addBox, SIGNAL(adminAdded()), this, SLOT(onAdminAdded())); + App::wnd()->replaceLayer(_addBox); + } +} + +void MembersBox::onAdminAdded() { + if (!_addBox) return; + _addBox->onClose(); + _addBox = 0; + _inner.load(); +} + +void MembersBox::hideAll() { + ItemListBox::hideAll(); + _add.hide(); + _done.hide(); +} + +void MembersBox::showAll() { + ItemListBox::showAll(); + if (_inner.channel()->amCreator() && _inner.isLoaded() && (_inner.channel()->count < cMaxGroupCount() || !_inner.channel()->isPublic())) { + _add.show(); + } else { + _add.hide(); + } + _done.show(); +} + +void MembersBox::showDone() { + setFocus(); +} + NewGroupBox::NewGroupBox() : AbstractBox(), _group(this, qsl("group_type"), 0, lang(lng_create_group_title), true), _channel(this, qsl("group_type"), 1, lang(lng_create_channel_title)), _aboutGroupWidth(width() - st::newGroupPadding.left() - st::newGroupPadding.right() - st::rbDefFlat.textLeft), -_aboutGroup(st::normalFont, lang(lng_create_group_about), _defaultOptions, _aboutGroupWidth), +_aboutGroup(st::normalFont, lng_create_group_about(lt_count, QString::number(cMaxGroupCount())), _defaultOptions, _aboutGroupWidth), _aboutChannel(st::normalFont, lang(lng_create_channel_about), _defaultOptions, _aboutGroupWidth), _next(this, lang(lng_create_group_next), st::btnSelectDone), _cancel(this, lang(lng_cancel), st::btnSelectCancel) { @@ -1900,6 +2521,10 @@ void SetupChannelBox::mousePressEvent(QMouseEvent *e) { } } +void SetupChannelBox::leaveEvent(QEvent *e) { + updateSelected(QCursor::pos()); +} + void SetupChannelBox::updateSelected(const QPoint &cursorGlobalPosition) { QPoint p(mapFromGlobal(cursorGlobalPosition)); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index c326f062f..b885844fb 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -25,7 +25,14 @@ enum CreatingGroupType { CreatingGroupChannel, }; -class ContactsInner : public QWidget, public RPCSender { +enum MembersFilter { + MembersFilterRecent, + MembersFilterAdmins, +}; +typedef QMap MembersAlreadyIn; + +class ConfirmBox; +class ContactsInner : public TWidget, public RPCSender { Q_OBJECT private: @@ -35,7 +42,7 @@ private: public: ContactsInner(CreatingGroupType creating = CreatingGroupNone); - ContactsInner(ChannelData *channel); + ContactsInner(ChannelData *channel, MembersFilter channelFilter = MembersFilterRecent, const MembersAlreadyIn &already = MembersAlreadyIn()); ContactsInner(ChatData *chat); ContactsInner(UserData *bot); void init(); @@ -47,7 +54,7 @@ public: void mousePressEvent(QMouseEvent *e); void resizeEvent(QResizeEvent *e); - void paintDialog(QPainter &p, PeerData *peer, ContactData *data, bool sel); + void paintDialog(Painter &p, PeerData *peer, ContactData *data, bool sel); void updateFilter(QString filter = QString()); void selectSkip(int32 dir); @@ -68,10 +75,14 @@ public: ChatData *chat() const; ChannelData *channel() const; + MembersFilter channelFilter() const; UserData *bot() const; CreatingGroupType creating() const; int32 selectedCount() const; + bool hasAlreadyMembersInChannel() const { + return !_already.isEmpty(); + } ~ContactsInner(); @@ -81,6 +92,7 @@ signals: void selectAllQuery(); void searchByUsername(); void chosenChanged(); + void adminAdded(); public slots: @@ -91,15 +103,25 @@ public slots: void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); void onAddBot(); + void onAddAdmin(); + void onNoAddAdminBox(QObject *obj); private: + void addAdminDone(const MTPBool &result, mtpRequestId req); + bool addAdminFail(const RPCError &error, mtpRequestId req); + ChatData *_chat; ChannelData *_channel; + MembersFilter _channelFilter; UserData *_bot; CreatingGroupType _creating; + MembersAlreadyIn _already; ChatData *_addToChat; + UserData *_addAdmin; + mtpRequestId _addAdminRequestId; + ConfirmBox *_addAdminBox; int32 _time; @@ -148,6 +170,7 @@ public: ContactsBox(); ContactsBox(const QString &name, const QImage &photo); // group creation ContactsBox(ChannelData *channel); // channel setup + ContactsBox(ChannelData *channel, MembersFilter filter, const MembersAlreadyIn &already); ContactsBox(ChatData *chat); ContactsBox(UserData *bot); void keyPressEvent(QKeyEvent *e); @@ -160,6 +183,10 @@ public: _filter.setFocus(); } +signals: + + void adminAdded(); + public slots: void onFilterUpdate(); @@ -187,6 +214,7 @@ private: FlatInput _filter; FlatButton _next, _cancel; + MembersFilter _membersFilter; void peopleReceived(const MTPcontacts_Found &result, mtpRequestId req); bool peopleFailed(const RPCError &error, mtpRequestId req); @@ -211,6 +239,155 @@ private: bool creationFail(const RPCError &e); }; +class MembersInner : public TWidget, public RPCSender { + Q_OBJECT + +private: + + struct MemberData; + +public: + + MembersInner(ChannelData *channel, MembersFilter filter); + + void paintEvent(QPaintEvent *e); + void enterEvent(QEvent *e); + void leaveEvent(QEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + + void paintDialog(Painter &p, PeerData *peer, MemberData *data, bool sel, bool kickSel, bool kickDown); + + void selectSkip(int32 dir); + void selectSkipPage(int32 h, int32 dir); + + void loadProfilePhotos(int32 yFrom); + void chooseParticipant(); + + void refresh(); + + ChannelData *channel() const; + MembersFilter filter() const; + + void load(); + bool isLoaded() const { + return !_loading; + } + + QMap already() const; + + ~MembersInner(); + +signals: + + void mustScrollTo(int ymin, int ymax); + + void loaded(); + +public slots: + + void updateSel(); + void peerUpdated(PeerData *peer); + void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); + void onKickConfirm(); + void onKickBoxDestroyed(QObject *obj); + +private: + + void clearSel(); + MemberData *data(int32 index); + + void membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req); + bool membersFailed(const RPCError &error, mtpRequestId req); + + void kickDone(const MTPUpdates &result, mtpRequestId req); + void kickAdminDone(const MTPBool &result, mtpRequestId req); + bool kickFail(const RPCError &error, mtpRequestId req); + void removeKicked(); + + void clear(); + + ChannelData *_channel; + MembersFilter _filter; + + QString _kickText; + int32 _time, _kickWidth; + + int32 _sel, _kickSel, _kickDown; + bool _mouseSel; + + UserData *_kickConfirm; + mtpRequestId _kickRequestId; + + ConfirmBox *_kickBox; + + enum MemberRole { + MemberRoleNone, + MemberRoleSelf, + MemberRoleCreator, + MemberRoleEditor, + MemberRoleModerator, + MemberRoleKicked + }; + + struct MemberData { + Text name; + QString online; + bool canKick; + }; + + bool _loading; + mtpRequestId _loadingRequestId; + typedef QVector MemberRows; + typedef QVector MemberDates; + typedef QVector MemberRoles; + typedef QVector MemberDatas; + MemberRows _rows; + MemberDates _dates; + MemberRoles _roles; + MemberDatas _datas; + + QPoint _lastMousePos; + +}; + +class MembersBox : public ItemListBox { + Q_OBJECT + +public: + + MembersBox(ChannelData *channel, MembersFilter filter); + void keyPressEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); + + void setInnerFocus() { + setFocus(); + } + +public slots: + + void onLoaded(); + void onScroll(); + + void onAdd(); + void onAdminAdded(); + +protected: + + void hideAll(); + void showAll(); + void showDone(); + +private: + + MembersInner _inner; + FlatButton _add, _done; + + ContactsBox *_addBox; +}; + class NewGroupBox : public AbstractBox { Q_OBJECT @@ -319,6 +496,7 @@ public: void resizeEvent(QResizeEvent *e); void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); + void leaveEvent(QEvent *e); void closePressed(); diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 5ae743d90..7b4010c95 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -105,7 +105,7 @@ _phone(phone), _fname(fname), _lname(lname), _replyTo(replyTo) { _compressed.hide(); - _name = _fname + QChar(' ') + _lname; + _name = lng_full_name(lt_first_name, _fname, lt_last_name, _lname); _namew = st::mediaFont->m.width(_name); _size = _phone; _textw = qMax(_namew, st::mediaFont->m.width(_size)); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 0407a0c57..02a6eba22 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -228,25 +228,29 @@ void DialogsListWidget::paintEvent(QPaintEvent *e) { } } -void DialogsListWidget::peopleResultPaint(PeerData *peer, QPainter &p, int32 w, bool act, bool sel) const { +void DialogsListWidget::peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool act, bool sel) const { QRect fullRect(0, 0, w, st::dlgHeight); p.fillRect(fullRect, (act ? st::dlgActiveBG : (sel ? st::dlgHoverBG : st::dlgBG))->b); History *history = App::history(peer->id); - p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, history->peer->photo->pix(st::dlgPhotoSize)); + p.drawPixmap(st::dlgPaddingHor, st::dlgPaddingVer, peer->photo->pix(st::dlgPhotoSize)); int32 nameleft = st::dlgPaddingHor + st::dlgPhotoSize + st::dlgPhotoPadding; int32 namewidth = w - nameleft - st::dlgPaddingHor; QRect rectForName(nameleft, st::dlgPaddingVer + st::dlgNameTop, namewidth, st::msgNameFont->height); // draw chat icon - if (history->peer->isChat()) { + if (peer->isChat()) { p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgPos.x(), rectForName.top() + st::dlgChatImgPos.y()), App::sprite(), (act ? st::dlgActiveChatImg : st::dlgChatImg)); rectForName.setLeft(rectForName.left() + st::dlgImgSkip); - } else if (history->peer->isChannel()) { + } else if (peer->isChannel()) { p.drawPixmap(QPoint(rectForName.left() + st::dlgChannelImgPos.x(), rectForName.top() + st::dlgChannelImgPos.y()), App::sprite(), (act ? st::dlgActiveChannelImg : st::dlgChannelImg)); rectForName.setLeft(rectForName.left() + st::dlgImgSkip); + if (peer->asChannel()->isVerified()) { + rectForName.setWidth(rectForName.width() - st::verifiedCheck.pxWidth() - st::verifiedCheckPos.x()); + p.drawSprite(rectForName.topLeft() + QPoint(qMin(peer->dialogName().maxWidth(), rectForName.width()), 0) + st::verifiedCheckPos, (act ? st::verifiedCheckInv : st::verifiedCheck)); + } } QRect tr(nameleft, st::dlgPaddingVer + st::dlgFont->height + st::dlgSep, namewidth, st::dlgFont->height); @@ -273,7 +277,7 @@ void DialogsListWidget::peopleResultPaint(PeerData *peer, QPainter &p, int32 w, peer->dialogName().drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } -void DialogsListWidget::searchInPeerPaint(QPainter &p, int32 w) const { +void DialogsListWidget::searchInPeerPaint(Painter &p, int32 w) const { QRect fullRect(0, 0, w, st::dlgHeight); p.fillRect(fullRect, st::dlgBG->b); @@ -450,6 +454,8 @@ void DialogsListWidget::removePeer(PeerData *peer) { History *history = App::history(peer->id); dialogs.del(peer); history->dialogs = History::DialogLinks(); + history->clearNotifications(); + if (App::wnd()) App::wnd()->notifyClear(history); if (contacts.list.rowByPeer.constFind(peer->id) != contacts.list.rowByPeer.cend()) { if (contactsNoDialogs.list.rowByPeer.constFind(peer->id) == contactsNoDialogs.list.rowByPeer.cend()) { contactsNoDialogs.addByName(App::history(peer->id)); @@ -1665,8 +1671,6 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque } break; } - unreadCountsReceived(*dlgList); - if (!_contactsRequest) { _contactsRequest = MTP::send(MTPcontacts_GetContacts(MTP_string("")), rpcDone(&DialogsWidget::contactsReceived), rpcFail(&DialogsWidget::contactsFailed)); } @@ -1674,6 +1678,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque if (_dialogsRequest == req) { _dialogsCount = count; if (dlgList) { + unreadCountsReceived(*dlgList); list.dialogsReceived(*dlgList); onListScroll(); @@ -1693,6 +1698,7 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque } else if (_channelDialogsRequest == req) { //_channelDialogsCount = count; if (dlgList) { + unreadCountsReceived(*dlgList); list.dialogsReceived(*dlgList); onListScroll(); diff --git a/Telegram/SourceFiles/dialogswidget.h b/Telegram/SourceFiles/dialogswidget.h index 38d423a81..a1bc5227b 100644 --- a/Telegram/SourceFiles/dialogswidget.h +++ b/Telegram/SourceFiles/dialogswidget.h @@ -49,8 +49,8 @@ public: void enterEvent(QEvent *e); void leaveEvent(QEvent *e); - void peopleResultPaint(PeerData *peer, QPainter &p, int32 w, bool act, bool sel) const; - void searchInPeerPaint(QPainter &p, int32 w) const; + void peopleResultPaint(PeerData *peer, Painter &p, int32 w, bool act, bool sel) const; + void searchInPeerPaint(Painter &p, int32 w) const; void selectSkip(int32 direction); void selectSkipPage(int32 pixels, int32 direction); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index b67de76f9..c94e20860 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -647,11 +647,12 @@ void ChannelHistory::addNewGroup(const MTPMessageGroup &group) { } HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { - if (_joinedMessage || peer->asChannel()->haveLeft() || peer->asChannel()->wasKicked()) return _joinedMessage; + if (_joinedMessage || !peer->asChannel()->amIn()) return _joinedMessage; UserData *inviter = (peer->asChannel()->inviter > 0) ? App::userLoaded(peer->asChannel()->inviter) : 0; if (!inviter) return 0; + if (peerToUser(inviter->id) == MTP::authedId()) unread = false; int32 flags = (unread ? MTPDmessage_flag_unread : 0); QDateTime inviteDate = peer->asChannel()->inviteDate; if (unread) _maxReadMessageDate = inviteDate; @@ -1725,8 +1726,11 @@ void History::newItemAdded(HistoryItem *item) { if (item->out()) { if (unreadBar) unreadBar->destroy(); } else if (item->unread()) { - notifies.push_back(item); - App::main()->newUnreadMsg(this, item); + bool skip = false; + if (!isChannel() || peer->asChannel()->amIn()) { + notifies.push_back(item); + App::main()->newUnreadMsg(this, item); + } } } @@ -2310,7 +2314,7 @@ void History::setLastMessage(HistoryItem *msg) { } void History::setPosInDialogsDate(const QDateTime &date) { - bool updateDialog = (App::main() && (!peer->isChannel() || peer->asChannel()->amCreator() || (!peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked()))); + bool updateDialog = (App::main() && (!peer->isChannel() || peer->asChannel()->amIn())); if (!lastMsgDate.isNull() && lastMsgDate >= date) { if (!updateDialog || !dialogs.isEmpty()) { return; @@ -4493,7 +4497,24 @@ HistoryContact::HistoryContact(int32 userId, const QString &first, const QString App::regSharedContactPhone(userId, phone); _maxw = st::mediaMaxWidth; - name.setText(st::mediaFont, (first + ' ' + last).trimmed(), _textNameOptions); + name.setText(st::mediaFont, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); + + phonew = st::mediaFont->m.width(phone); + + if (contact) { + contact->photo->load(); + } +} + +HistoryContact::HistoryContact(int32 userId, const QString &fullname, const QString &phone) : HistoryMedia(0) +, userId(userId) +, phone(App::formatPhone(phone)) +, contact(App::userLoaded(userId)) +{ + App::regSharedContactPhone(userId, phone); + + _maxw = st::mediaMaxWidth; + name.setText(st::mediaFont, fullname.trimmed(), _textNameOptions); phonew = st::mediaFont->m.width(phone); @@ -4589,14 +4610,7 @@ void HistoryContact::getState(TextLinkPtr &lnk, HistoryCursorState &state, int32 } HistoryMedia *HistoryContact::clone() const { - QStringList names = name.original(0, 0xFFFF, false).split(QChar(' '), QString::SkipEmptyParts); - if (names.isEmpty()) { - names.push_back(QString()); - } - QString fname = names.front(); - names.pop_front(); - HistoryContact *result = new HistoryContact(userId, fname, names.join(QChar(' ')), phone); - return result; + return new HistoryContact(userId, name.original(0, 0xFFFF, false), phone); } void HistoryContact::draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const { @@ -5966,7 +5980,7 @@ void HistoryImageLink::getState(TextLinkPtr &lnk, HistoryCursorState &state, int skipx = st::mediaPadding.left(); if (reply) { skipy = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); - } if (fwd) { + } else if (fwd) { skipy = st::msgServiceNameFont->height + st::msgPadding.top(); } if (parent->displayFromName()) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 3286aaf2e..4d367734d 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -827,7 +827,7 @@ public: } bool unread() const { if ((out() && (id > 0 && id < _history->outboxReadBefore)) || (!out() && id > 0 && id < _history->inboxReadBefore)) return false; - return (id > 0) ? true : (_flags & MTPDmessage_flag_unread); + return (id > 0 && !out()) ? true : (_flags & MTPDmessage_flag_unread); } bool notifyByFrom() const { return _flags & MTPDmessage_flag_notify_by_from; @@ -1332,6 +1332,7 @@ class HistoryContact : public HistoryMedia { public: HistoryContact(int32 userId, const QString &first, const QString &last, const QString &phone); + HistoryContact(int32 userId, const QString &fullname, const QString &phone); void initDimensions(const HistoryItem *parent); void draw(Painter &p, const HistoryItem *parent, bool selected, int32 width) const; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 3a2757f81..888c53540 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -3867,6 +3867,10 @@ bool HistoryWidget::joinFail(const RPCError &error, mtpRequestId req) { if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false; if (_unblockRequest == req) _unblockRequest = 0; + if (error.type() == qstr("CHANNEL_PRIVATE")) { + App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_not_accessible), true)); + return true; + } return false; } @@ -4221,7 +4225,7 @@ void HistoryWidget::sendBotCommand(const QString &cmd, MsgId replyTo) { // reply QString toSend = cmd; PeerData *bot = _peer->isUser() ? _peer : (App::hoveredLinkItem() ? (App::hoveredLinkItem()->toHistoryForwarded() ? App::hoveredLinkItem()->toHistoryForwarded()->fromForwarded() : App::hoveredLinkItem()->from()) : 0); - if (!bot->isUser() || !bot->asUser()->botInfo) bot = 0; + if (bot && (!bot->isUser() || !bot->asUser()->botInfo)) bot = 0; QString username = bot ? bot->asUser()->username : QString(); int32 botStatus = _peer->isChat() ? _peer->asChat()->botStatus : (_peer->isChannel() ? _peer->asChannel()->botStatus : -1); if (!replyTo && toSend.indexOf('@') < 2 && !username.isEmpty() && (botStatus == 0 || botStatus == 2)) { @@ -4352,7 +4356,7 @@ bool HistoryWidget::canSendMessages(PeerData *peer) const { } else if (peer->isChat()) { return !peer->asChat()->isForbidden && !peer->asChat()->haveLeft; } else if (peer->isChannel()) { - return !peer->asChannel()->isForbidden && !peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked() && (peer->asChannel()->canPublish() || !peer->asChannel()->isBroadcast()); + return peer->asChannel()->amIn() && (peer->asChannel()->canPublish() || !peer->asChannel()->isBroadcast()); } } return false; @@ -4376,7 +4380,7 @@ bool HistoryWidget::isBlocked() const { } bool HistoryWidget::isJoinChannel() const { - return _peer && _peer->isChannel() && !_peer->asChannel()->amParticipant(); + return _peer && _peer->isChannel() && !_peer->asChannel()->amIn(); } bool HistoryWidget::isMuteUnmute() const { diff --git a/Telegram/SourceFiles/intro/introsignup.cpp b/Telegram/SourceFiles/intro/introsignup.cpp index cc4bcdde1..ee24a7513 100644 --- a/Telegram/SourceFiles/intro/introsignup.cpp +++ b/Telegram/SourceFiles/intro/introsignup.cpp @@ -31,13 +31,18 @@ IntroSignup::IntroSignup(IntroWidget *parent) : IntroStage(parent), errorAlpha(0), a_photo(0), next(this, lang(lng_intro_finish), st::btnIntroNext), first(this, st::inpIntroName, lang(lng_signup_firstname)), - last(this, st::inpIntroName, lang(lng_signup_lastname)) { + last(this, st::inpIntroName, lang(lng_signup_lastname)), + _invertOrder(langFirstNameGoesSecond()) { setVisible(false); setGeometry(parent->innerRect()); connect(&next, SIGNAL(clicked()), this, SLOT(onSubmitName())); connect(&checkRequest, SIGNAL(timeout()), this, SLOT(onCheckRequest())); + if (_invertOrder) { + setTabOrder(&last, &first); + } + setMouseTracking(true); } @@ -102,7 +107,12 @@ void IntroSignup::paintEvent(QPaintEvent *e) { if (animating() || error.length()) { p.setOpacity(errorAlpha.current()); - QRect errRect((width() - st::introErrWidth) / 2, (last.y() + last.height() + next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); + QRect errRect; + if (_invertOrder) { + errRect = QRect((width() - st::introErrWidth) / 2, (first.y() + first.height() + next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); + } else { + errRect = QRect((width() - st::introErrWidth) / 2, (last.y() + last.height() + next.y() - st::introErrHeight) / 2, st::introErrWidth, st::introErrHeight); + } p.setFont(st::introErrFont->f); p.setPen(st::introErrColor->p); p.drawText(errRect, error, QTextOption(style::al_center)); @@ -135,8 +145,13 @@ void IntroSignup::resizeEvent(QResizeEvent *e) { _phTop = st::introTextTop + st::introTextSize.height() + st::introCountry.top; if (e->oldSize().width() != width()) { next.move((width() - next.width()) / 2, st::introBtnTop); - first.move((width() - next.width()) / 2 + next.width() - first.width(), _phTop); - last.move((width() - next.width()) / 2 + next.width() - last.width(), first.y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); + if (_invertOrder) { + last.move((width() - next.width()) / 2 + next.width() - last.width(), _phTop); + first.move((width() - next.width()) / 2 + next.width() - first.width(), last.y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); + } else { + first.move((width() - next.width()) / 2 + next.width() - first.width(), _phTop); + last.move((width() - next.width()) / 2 + next.width() - last.width(), first.y() + st::introCountry.height + st::introCountry.ptrSize.height() + st::introPhoneTop); + } } textRect = QRect((width() - st::introTextSize.width()) / 2, st::introTextTop, st::introTextSize.width(), st::introTextSize.height()); } @@ -175,7 +190,11 @@ bool IntroSignup::animStep(float64 ms) { void IntroSignup::activate() { show(); - first.setFocus(); + if (_invertOrder) { + last.setFocus(); + } else { + first.setFocus(); + } } void IntroSignup::deactivate() { @@ -196,7 +215,11 @@ void IntroSignup::onCheckRequest() { if (!first.isEnabled()) { first.setDisabled(false); last.setDisabled(false); - last.setFocus(); + if (_invertOrder) { + first.setFocus(); + } else { + last.setFocus(); + } } } } @@ -241,7 +264,11 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) { return true; } else if (error.type().startsWith(qsl("FLOOD_WAIT_"))) { showError(lang(lng_flood_error)); - last.setFocus(); + if (_invertOrder) { + first.setFocus(); + } else { + last.setFocus(); + } return true; } if (cDebug()) { // internal server error @@ -249,7 +276,11 @@ bool IntroSignup::nameSubmitFail(const RPCError &error) { } else { showError(lang(lng_server_error)); } - first.setFocus(); + if (_invertOrder) { + last.setFocus(); + } else { + first.setFocus(); + } return false; } @@ -258,12 +289,22 @@ void IntroSignup::onInputChange() { } void IntroSignup::onSubmitName(bool force) { - if ((first.hasFocus() || first.text().trimmed().length()) && !last.text().trimmed().length()) { - last.setFocus(); - return; - } else if (!first.text().trimmed().length()) { - first.setFocus(); - return; + if (_invertOrder) { + if ((last.hasFocus() || last.text().trimmed().length()) && !first.text().trimmed().length()) { + first.setFocus(); + return; + } else if (!last.text().trimmed().length()) { + last.setFocus(); + return; + } + } else { + if ((first.hasFocus() || first.text().trimmed().length()) && !last.text().trimmed().length()) { + last.setFocus(); + return; + } else if (!first.text().trimmed().length()) { + first.setFocus(); + return; + } } if (!force && !first.isEnabled()) return; diff --git a/Telegram/SourceFiles/intro/introsignup.h b/Telegram/SourceFiles/intro/introsignup.h index d4775266e..9d68fa10d 100644 --- a/Telegram/SourceFiles/intro/introsignup.h +++ b/Telegram/SourceFiles/intro/introsignup.h @@ -72,5 +72,7 @@ private: QString firstName, lastName; mtpRequestId sentRequest; + bool _invertOrder; + QTimer checkRequest; }; diff --git a/Telegram/SourceFiles/lang.h b/Telegram/SourceFiles/lang.h index e99c42dca..9091f2806 100644 --- a/Telegram/SourceFiles/lang.h +++ b/Telegram/SourceFiles/lang.h @@ -139,3 +139,19 @@ public: QString translate(const char *context, const char *sourceText, const char *disambiguation = 0, int n = -1) const; }; + +inline bool langFirstNameGoesSecond() { + QString fullname = lang(lng_full_name__tagged); + for (const QChar *s = fullname.constData(), *ch = s, *e = ch + fullname.size(); ch != e;) { + if (*ch == TextCommand) { + if (ch + 3 < e && (ch + 1)->unicode() == TextCommandLangTag && *(ch + 3) == TextCommand) { + if ((ch + 2)->unicode() == 0x0020 + lt_last_name) { + return true; + } else if ((ch + 2)->unicode() == 0x0020 + lt_first_name) { + break; + } + } + } + } + return false; +} diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 5a2dd957c..537d154e8 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -864,7 +864,7 @@ void MainWidget::addParticipants(PeerData *chatOrChannel, const QVector::const_iterator i = users.cbegin(), e = users.cend(); i != e; ++i) { inputUsers.push_back((*i)->inputUser); } - MTP::send(MTPchannels_InviteToChannel(chatOrChannel->asChannel()->inputChannel, MTP_vector(inputUsers)), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::addParticipantsFail), 0, 5); + MTP::send(MTPchannels_InviteToChannel(chatOrChannel->asChannel()->inputChannel, MTP_vector(inputUsers)), rpcDone(&MainWidget::inviteToChannelDone, chatOrChannel->asChannel()), rpcFail(&MainWidget::addParticipantsFail), 0, 5); } } @@ -960,15 +960,13 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu showDialogs(); } } else if (peer->isChannel()) { - if (peer->asChannel()->inviter > 0) { - if (!peer->asChannel()->isForbidden && !peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked()) { - if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) { - History *h = App::history(peer->id); - h->clear(true); - h->addNewerSlice(QVector(), 0); - h->asChannelHistory()->insertJoinedMessage(true); - history.peerMessagesUpdated(h->peer->id); - } + if (peer->asChannel()->inviter > 0 && peer->asChannel()->amIn()) { + if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) { + History *h = App::history(peer->id); + h->clear(true); + h->addNewerSlice(QVector(), 0); + h->asChannelHistory()->insertJoinedMessage(true); + history.peerMessagesUpdated(h->peer->id); } } } else { @@ -990,12 +988,10 @@ void MainWidget::checkedHistory(PeerData *peer, const MTPmessages_Messages &resu } } if (!h->lastMsgDate.isNull() && h->loadedAtBottom()) { - if (peer->isChannel() && peer->asChannel()->inviter > 0 && h->lastMsgDate <= peer->asChannel()->inviteDate) { - if (!peer->asChannel()->isForbidden && !peer->asChannel()->haveLeft() && !peer->asChannel()->wasKicked()) { - if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) { - h->asChannelHistory()->insertJoinedMessage(true); - history.peerMessagesUpdated(h->peer->id); - } + if (peer->isChannel() && peer->asChannel()->inviter > 0 && h->lastMsgDate <= peer->asChannel()->inviteDate && peer->asChannel()->amIn()) { + if (UserData *from = App::userLoaded(peer->asChannel()->inviter)) { + h->asChannelHistory()->insertJoinedMessage(true); + history.peerMessagesUpdated(h->peer->id); } } } @@ -1264,8 +1260,12 @@ void MainWidget::saveRecentHashtags(const QString &text) { void MainWidget::readServerHistory(History *hist, bool force) { if (!hist || (!force && !hist->unreadCount)) return; - ReadRequests::const_iterator i = _readRequests.constFind(hist->peer); MsgId upTo = hist->inboxRead(0); + if (hist->isChannel() && !hist->peer->asChannel()->amIn()) { + return; // no read request for channels that I didn't koin + } + + ReadRequests::const_iterator i = _readRequests.constFind(hist->peer); if (i == _readRequests.cend()) { sendReadRequest(hist->peer, upTo); } else { @@ -1670,7 +1670,7 @@ void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMess App::emitPeerUpdated(); } } - if (History *h = App::historyLoaded(peer->id)) { + if (History *h = App::historyLoaded(peer ? peer->id : 0)) { if (!h->lastMsg) { checkPeerHistory(peer); } @@ -2651,6 +2651,11 @@ void MainWidget::sentUpdatesReceived(uint64 randomId, const MTPUpdates &result) App::emitPeerUpdated(); } +void MainWidget::inviteToChannelDone(ChannelData *channel, const MTPUpdates &updates) { + sentUpdatesReceived(updates); + channel->updateFull(true); +} + void MainWidget::msgUpdated(PeerId peer, const HistoryItem *msg) { if (!msg) return; history.msgUpdated(peer, msg); @@ -3504,6 +3509,8 @@ void MainWidget::openPeerByName(const QString &username, bool toProfile, const Q if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { peer->asUser()->botInfo->startGroupToken = startToken; App::wnd()->showLayer(new ContactsBox(peer->asUser())); + } else if (peer->isChannel()) { + showPeerHistory(peer->id, ShowAtUnreadMsgId); } else { showPeerProfile(peer); } @@ -3574,6 +3581,8 @@ void MainWidget::usernameResolveDone(QPair toProfileStartToken, c if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !toProfileStartToken.second.isEmpty()) { peer->asUser()->botInfo->startGroupToken = toProfileStartToken.second; App::wnd()->showLayer(new ContactsBox(peer->asUser())); + } else if (peer->isChannel()) { + showPeerHistory(peer->id, ShowAtUnreadMsgId); } else { showPeerProfile(peer); } @@ -4504,7 +4513,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (ChannelData *channel = App::channelLoaded(d.vchannel_id.v)) { App::markPeerUpdated(channel); channel->inviter = 0; - if (channel->isForbidden || channel->wasKicked() || channel->haveLeft()) { + if (!channel->amIn()) { dialogs.removePeer(channel); if (History *h = App::historyLoaded(channel->id)) { h->clear(true); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 6d93af396..a7ca59a1e 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -227,6 +227,7 @@ public: void sentUpdatesReceived(const MTPUpdates &updates) { return sentUpdatesReceived(0, updates); } + void inviteToChannelDone(ChannelData *channel, const MTPUpdates &updates); void msgUpdated(PeerId peer, const HistoryItem *msg); void historyToDown(History *hist); void dialogsToUp(); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 98a6dce58..afc01138d 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -44,13 +44,18 @@ namespace { MediaView *_view; }; - TextParseOptions _captionTextOptions = { TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags 0, // maxw 0, // maxh Qt::LayoutDirectionAuto, // dir }; + TextParseOptions _captionBotOptions = { + TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText | TextParseBotCommands, // flags + 0, // maxw + 0, // maxh + Qt::LayoutDirectionAuto, // dir + }; } MediaView::MediaView() : TWidget(App::wnd()), @@ -763,7 +768,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { _caption = Text(); if (HistoryMessage *itemMsg = item ? item->toHistoryMessage() : 0) { if (HistoryPhoto *photoMsg = dynamic_cast(itemMsg->getMedia())) { - _caption.setText(st::mvCaptionFont, photoMsg->captionForClone().original(0, 0xFFFF), _captionTextOptions); + _caption.setText(st::mvCaptionFont, photoMsg->captionForClone().original(0, 0xFFFF), (item->from()->isUser() && item->from()->asUser()->botInfo) ? _captionBotOptions : _captionTextOptions); } } diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index 632da334c..2e7444363 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -47,8 +47,9 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee _invitationLink(this, qsl("telegram.me/joinchat/")), _botSettings(this, lang(lng_profile_bot_settings)), _botHelp(this, lang(lng_profile_bot_help)), - _username(this, qsl("https://telegram.me/") + (_peerChannel ? _peerChannel->username : QString())), - _editLink(this, lang((_peerChannel && _peerChannel->isPublic()) ? lng_profile_edit_public_link : lng_profile_create_public_link)), + _username(this, (_peerChannel && _peerChannel->isPublic()) ? (qsl("telegram.me/") + _peerChannel->username) : lang(lng_profile_create_public_link)), + _members(this, lng_channel_members_link(lt_count, (_peerChannel && _peerChannel->count > 0) ? _peerChannel->count : 1)), + _admins(this, lng_channel_admins_link(lt_count, (_peerChannel && _peerChannel->adminsCount > 0) ? _peerChannel->adminsCount : 1)), // about _about(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()), @@ -121,6 +122,8 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee connect(&_createInvitationLink, SIGNAL(clicked()), this, SLOT(onCreateInvitationLink())); connect(&_invitationLink, SIGNAL(clicked()), this, SLOT(onInvitationLink())); connect(&_username, SIGNAL(clicked()), this, SLOT(onPublicLink())); + connect(&_members, SIGNAL(clicked()), this, SLOT(onMembers())); + connect(&_admins, SIGNAL(clicked()), this, SLOT(onAdmins())); _invitationLink.setAcceptBoth(true); _username.setAcceptBoth(true); updateInvitationLink(); @@ -128,7 +131,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee if (_peerChat) { QString maxStr = lang(_uploadPhoto.textWidth() > _addParticipant.textWidth() ? lng_profile_set_group_photo : lng_profile_add_participant); _uploadPhoto.setAutoFontSize(st::profileMinBtnPadding, maxStr); - _uploadPhoto.setAutoFontSize(st::profileMinBtnPadding, maxStr); + _addParticipant.setAutoFontSize(st::profileMinBtnPadding, maxStr); } else if (_peerUser) { QString maxStr; if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) { @@ -141,11 +144,12 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, const Pee _sendMessage.setAutoFontSize(st::profileMinBtnPadding, maxStr); _shareContact.setAutoFontSize(st::profileMinBtnPadding, maxStr); _inviteToGroup.setAutoFontSize(st::profileMinBtnPadding, maxStr); + } else if (_peerChannel && _amCreator) { + _uploadPhoto.setAutoFontSize(st::profileMinBtnPadding, lang(lng_profile_set_group_photo)); } connect(&_botSettings, SIGNAL(clicked()), this, SLOT(onBotSettings())); connect(&_botHelp, SIGNAL(clicked()), this, SLOT(onBotHelp())); - connect(&_editLink, SIGNAL(clicked()), this, SLOT(onEditPublicLink())); connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId))); connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId))); @@ -340,9 +344,9 @@ bool ProfileInner::blockFail(const RPCError &error) { } void ProfileInner::onAddParticipant() { - if (!_peerChat && !_peerChannel) return; + if (!_peerChat) return; - App::wnd()->showLayer(_peerChat ? (new ContactsBox(_peerChat)) : (new ContactsBox(_peerChannel))); + App::wnd()->showLayer(new ContactsBox(_peerChat)); } void ProfileInner::onUpdatePhotoCancel() { @@ -398,10 +402,24 @@ void ProfileInner::onInvitationLink() { } void ProfileInner::onPublicLink() { - if (!_peerChannel || !_peerChannel->isPublic()) return; + if (!_peerChannel) return; + + if (_peerChannel->isPublic()) { + QApplication::clipboard()->setText(qsl("https://telegram.me/") + _peerChannel->username); + App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_public_link_copied), true)); + } else { + App::wnd()->showLayer(new SetupChannelBox(_peerChannel, true)); + } +} - QApplication::clipboard()->setText(qsl("https://telegram.me/") + _peerChannel->username); - App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_public_link_copied), true)); +void ProfileInner::onMembers() { + if (!_peerChannel) return; + App::wnd()->showLayer(new MembersBox(_peerChannel, MembersFilterRecent)); +} + +void ProfileInner::onAdmins() { + if (!_peerChannel) return; + App::wnd()->showLayer(new MembersBox(_peerChannel, MembersFilterAdmins)); } void ProfileInner::onCreateInvitationLink() { @@ -459,6 +477,9 @@ void ProfileInner::onFullPeerUpdated(PeerData *peer) { resizeEvent(0); } else if (_peerChannel) { updateInvitationLink(); + _members.setText(lng_channel_members_link(lt_count, (_peerChannel->count > 0) ? _peerChannel->count : 1)); + _admins.setText(lng_channel_admins_link(lt_count, (_peerChannel->adminsCount > 0) ? _peerChannel->adminsCount : 1)); + _onlineText = lng_chat_status_members(lt_count, _peerChannel->count > 0 ? _peerChannel->count : 1); if (_peerChannel->about.isEmpty()) { _about = Text(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()); } else { @@ -497,10 +518,6 @@ void ProfileInner::onBotHelp() { updateBotLinksVisibility(); } -void ProfileInner::onEditPublicLink() { - App::wnd()->showLayer(new SetupChannelBox(_peerChannel, true), true); -} - void ProfileInner::peerUpdated(PeerData *data) { if (data == _peer) { PhotoData *photo = 0; @@ -518,6 +535,9 @@ void ProfileInner::peerUpdated(PeerData *data) { if (_peerChannel->isPublic() != _invitationLink.isHidden()) { peerUsernameChanged(); } + _members.setText(lng_channel_members_link(lt_count, (_peerChannel->count > 0) ? _peerChannel->count : 1)); + _admins.setText(lng_channel_admins_link(lt_count, (_peerChannel->adminsCount > 0) ? _peerChannel->adminsCount : 1)); + _onlineText = lng_chat_status_members(lt_count, _peerChannel->count > 0 ? _peerChannel->count : 1); } _photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr(); if (_peer->name != _nameCache) { @@ -600,7 +620,7 @@ void ProfileInner::reorderParticipants() { if (_peerUser) { _onlineText = App::onlineText(_peerUser, t, true); } else if (_peerChannel) { - _onlineText = _peerChannel->count ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(lng_channel_status); + _onlineText = lng_chat_status_members(lt_count, _peerChannel->count > 0 ? _peerChannel->count : 1); } else { _onlineText = lang(lng_chat_status_unaccessible); } @@ -615,12 +635,7 @@ void ProfileInner::start() { void ProfileInner::peerUsernameChanged() { if (_peerChannel) { - if (_peerChannel->isPublic()) { - _username.setText(qsl("https://telegram.me/") + _peerChannel->username); - _editLink.setText(lang(lng_profile_edit_public_link)); - } else { - _editLink.setText(lang(lng_profile_create_public_link)); - } + _username.setText(_peerChannel->isPublic() ? (qsl("telegram.me/") + _peerChannel->username) : lang(lng_profile_create_public_link)); resizeEvent(0); showAll(); } @@ -636,7 +651,7 @@ bool ProfileInner::event(QEvent *e) { } void ProfileInner::paintEvent(QPaintEvent *e) { - QPainter p(this); + Painter p(this); QRect r(e->rect()); p.setClipRect(r); @@ -657,8 +672,15 @@ void ProfileInner::paintEvent(QPaintEvent *e) { p.setOpacity(1); } } + + int32 namew = _width - st::profilePhotoSize - st::profileNameLeft; p.setPen(st::black->p); - _nameText.drawElided(p, _left + st::profilePhotoSize + st::profileNameLeft, top + st::profileNameTop, _width - st::profilePhotoSize - st::profileNameLeft); + if (_peerChannel && _peerChannel->isVerified()) { + namew -= st::verifiedCheckProfile.pxWidth() + st::verifiedCheckProfilePos.x(); + int32 cx = _left + st::profilePhotoSize + st::profileNameLeft + qMin(_nameText.maxWidth(), namew); + p.drawSprite(QPoint(cx, top + st::profileNameTop) + st::verifiedCheckProfilePos, st::verifiedCheckProfile); + } + _nameText.drawElided(p, _left + st::profilePhotoSize + st::profileNameLeft, top + st::profileNameTop, namew); p.setFont(st::profileStatusFont->f); int32 addbyname = 0; @@ -666,24 +688,22 @@ void ProfileInner::paintEvent(QPaintEvent *e) { addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); p.setPen(st::black->p); p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop + st::linkFont->ascent, '@' + _peerUser->username); + } else if (_peerChannel && (_peerChannel->isPublic() || _amCreator)) { + addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); } - p.setPen((_peerUser && App::onlineColorUse(_peerUser, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); - p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText); - if (_amCreator && ((_peerChat && !_peerChat->invitationUrl.isEmpty()) || (_peerChannel && !_peerChannel->invitationUrl.isEmpty()))) { - if (!_peerChannel || !_peerChannel->isPublic()) { - p.setPen(st::black->p); - p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _createInvitationLink.y() + st::linkFont->ascent, lang(lng_group_invite_link)); - } + if (!_peerChannel || (!_amCreator && !_peerChannel->amEditor() && !_peerChannel->amModerator())) { + p.setPen((_peerUser && App::onlineColorUse(_peerUser, l_time) ? st::profileOnlineColor : st::profileOfflineColor)->p); + p.drawText(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop + st::linkFont->ascent, _onlineText); } if (!_cancelPhoto.isHidden()) { p.setPen(st::profileOfflineColor->p); - p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + addbyname + st::linkFont->ascent, lang(lng_settings_uploading_photo)); + p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + st::linkFont->ascent, lang(lng_settings_uploading_photo)); } if (!_errorText.isEmpty()) { p.setFont(st::setErrFont->f); p.setPen(st::setErrColor->p); - p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + addbyname + st::profilePhoneFont->ascent, _errorText); + p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, _cancelPhoto.y() + st::profilePhoneFont->ascent, _errorText); } if (!_phoneText.isEmpty()) { p.setPen(st::black->p); @@ -702,20 +722,12 @@ void ProfileInner::paintEvent(QPaintEvent *e) { if (!_peerChannel || _amCreator) { top += _shareContact.height(); } - if (_peerChannel && (_amCreator || _peerChannel->isPublic())) { - if (!_amCreator) { - top += st::setLittleSkip; - } else { - top += st::setSectionSkip; - } - top += _editLink.height(); - } // about if (!_about.isEmpty()) { p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); - p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_about_section)); + p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(_peerChannel ? lng_profile_description_section : lng_profile_about_section)); top += st::profileHeaderSkip; _about.draw(p, _left, top, _width); @@ -728,6 +740,17 @@ void ProfileInner::paintEvent(QPaintEvent *e) { p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_settings_section)); top += st::profileHeaderSkip; + // invite link stuff + if (_amCreator && (!_peerChannel || !_peerChannel->isPublic())) { + if ((_peerChat && !_peerChat->invitationUrl.isEmpty()) || (_peerChannel && !_peerChannel->invitationUrl.isEmpty())) { + p.setPen(st::black); + p.setFont(st::linkFont); + p.drawText(_left, _invitationLink.y() + st::linkFont->ascent, lang(lng_group_invite_link)); + top += _invitationLink.height() + st::setLittleSkip; + } + top += _createInvitationLink.height() + st::setSectionSkip; + } + top += _enableNotifications.height(); // shared media @@ -764,7 +787,7 @@ void ProfileInner::paintEvent(QPaintEvent *e) { if (_peerUser || _peerChat) { top += st::setLittleSkip + _clearHistory.height(); } - if (_peerUser || _peerChat || !_amCreator) { + if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { top += st::setLittleSkip + _deleteConversation.height(); } if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) { @@ -874,7 +897,7 @@ void ProfileInner::updateSelected() { if (_peerUser || _peerChat) { partfrom = _clearHistory.y() + _clearHistory.height(); } - if (_peerUser || _peerChat || !_amCreator) { + if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { partfrom = _deleteConversation.y() + _deleteConversation.height(); } partfrom += st::profileHeaderSkip; @@ -1075,16 +1098,15 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { // profile top += st::profilePadding.top(); + int32 addbyname = 0; + if (_peerChannel && (_amCreator || _peerChannel->isPublic())) { + _username.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + st::profileStatusTop); + addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); + } + _members.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); + addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); + _admins.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + 2 * addbyname + st::profileStatusTop); if (_amCreator) { - if ((_peerChat && _peerChat->invitationUrl.isEmpty()) || (_peerChannel && _peerChannel->invitationUrl.isEmpty())) { - _createInvitationLink.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop); - } else { - _createInvitationLink.move(_left + _width - _createInvitationLink.width(), top + st::profilePhoneTop); - } - if (!_invitationText.isEmpty()) { - _invitationLink.setText(st::linkFont->m.elidedText(_invitationText, Qt::ElideRight, _width - st::profilePhotoSize - st::profilePhoneLeft)); - } - _invitationLink.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profilePhoneTop + st::linkFont->height * 1.2); _cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhotoSize - st::linkFont->height); } else { _cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhoneTop); @@ -1105,20 +1127,6 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { if (!_peerChannel || _amCreator) { top += _shareContact.height(); } - if (_peerChannel && (_amCreator || _peerChannel->isPublic())) { - if (!_amCreator) { - top += st::setLittleSkip; - } else { - top += st::setSectionSkip; - } - if (_peerChannel->isPublic()) { - _username.move(_left, top); - _editLink.move(_left + _width - _editLink.width(), top); - } else { - _editLink.move(_left, top); - } - top += _editLink.height(); - } // about if (!_about.isEmpty()) { @@ -1130,6 +1138,23 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { // settings top += st::profileHeaderSkip; + + // invite link stuff + int32 _inviteLinkTextWidth(st::linkFont->m.width(lang(lng_group_invite_link)) + st::linkFont->spacew); + if (_amCreator && (!_peerChannel || !_peerChannel->isPublic())) { + if (!_invitationText.isEmpty()) { + _invitationLink.setText(st::linkFont->m.elidedText(_invitationText, Qt::ElideRight, _width - _inviteLinkTextWidth)); + } + if ((_peerChat && !_peerChat->invitationUrl.isEmpty()) || (_peerChannel && !_peerChannel->invitationUrl.isEmpty())) { + _invitationLink.move(_left + _inviteLinkTextWidth, top); + top += _invitationLink.height() + st::setLittleSkip; + _createInvitationLink.move(_left, top); + } else { + _createInvitationLink.move(_left, top); + } + top += _createInvitationLink.height() + st::setSectionSkip; + } + _enableNotifications.move(_left, top); top += _enableNotifications.height(); // shared media @@ -1157,7 +1182,7 @@ void ProfileInner::resizeEvent(QResizeEvent *e) { if (_peerUser || _peerChat) { _clearHistory.move(_left, top); top += _clearHistory.height() + st::setLittleSkip; } - if (_peerUser || _peerChat || !_amCreator) { + if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { _deleteConversation.move(_left, top); top += _deleteConversation.height(); } if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) { @@ -1288,8 +1313,10 @@ int32 ProfileInner::countMinHeight() { } else if (_peerChannel) { if (_amCreator) { h = _deleteChannel.y() + _deleteChannel.height() + st::profileHeaderSkip; - } else { + } else if (_peerChannel->amIn()) { h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip; + } else { + h = _searchInPeer.y() + _searchInPeer.height() + st::profileHeaderSkip; } } return h; @@ -1309,7 +1336,7 @@ void ProfileInner::showAll() { } else { _clearHistory.hide(); } - if (_peerUser || _peerChat || !_amCreator) { + if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { _deleteConversation.show(); } else { _deleteConversation.hide(); @@ -1339,8 +1366,9 @@ void ProfileInner::showAll() { _blockUser.hide(); } _deleteChannel.hide(); - _editLink.hide(); _username.hide(); + _members.hide(); + _admins.hide(); } else if (_peerChat) { _sendMessage.hide(); _shareContact.hide(); @@ -1378,8 +1406,9 @@ void ProfileInner::showAll() { } _blockUser.hide(); _deleteChannel.hide(); - _editLink.hide(); _username.hide(); + _members.hide(); + _admins.hide(); } else if (_peerChannel) { _sendMessage.hide(); _shareContact.hide(); @@ -1387,7 +1416,6 @@ void ProfileInner::showAll() { if (_peerChannel->isForbidden) { _uploadPhoto.hide(); _cancelPhoto.hide(); - _addParticipant.hide(); _createInvitationLink.hide(); _invitationLink.hide(); } else { @@ -1413,25 +1441,26 @@ void ProfileInner::showAll() { _createInvitationLink.hide(); _invitationLink.hide(); } - if (_amCreator) { - _addParticipant.show(); - } else { - _addParticipant.hide(); - } } + _addParticipant.hide(); _blockUser.hide(); if (_amCreator) { _deleteChannel.show(); - _editLink.show(); } else { _deleteChannel.hide(); - _editLink.hide(); } - if (_peerChannel->isPublic()) { + if (_peerChannel->isPublic() || _amCreator) { _username.show(); } else { _username.hide(); } + if (_amCreator || _peerChannel->amEditor() || _peerChannel->amModerator()) { + _members.show(); + _admins.show(); + } else { + _members.hide(); + _admins.hide(); + } } _enableNotifications.show(); updateNotifySettings(); diff --git a/Telegram/SourceFiles/profilewidget.h b/Telegram/SourceFiles/profilewidget.h index b67795a26..667604e3d 100644 --- a/Telegram/SourceFiles/profilewidget.h +++ b/Telegram/SourceFiles/profilewidget.h @@ -110,11 +110,13 @@ public slots: void onCreateInvitationLinkSure(); void onPublicLink(); + void onMembers(); + void onAdmins(); + void onFullPeerUpdated(PeerData *peer); void onBotSettings(); void onBotHelp(); - void onEditPublicLink(); private: @@ -146,7 +148,7 @@ private: FlatButton _sendMessage, _shareContact, _inviteToGroup; LinkButton _cancelPhoto, _createInvitationLink, _invitationLink; QString _invitationText; - LinkButton _botSettings, _botHelp, _username, _editLink; + LinkButton _botSettings, _botHelp, _username, _members, _admins; Text _about; int32 _aboutTop, _aboutHeight; diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 51feeda62..dd53fdacb 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -2107,7 +2107,6 @@ bool psShowOpenWithMenu(int x, int y, const QString &file) { if (sel > 0) { if (sel <= handlers.size()) { IDataObject *dataObj = 0; - IEnumAssocHandlers *assocHandlers = 0; if (SUCCEEDED(pItem->BindToHandler(nullptr, BHID_DataObject, IID_PPV_ARGS(&dataObj))) && dataObj) { handlers.at(sel - 1).handler->Invoke(dataObj); dataObj->Release(); diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index ab15f05fa..6f2a9d304 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -129,7 +129,17 @@ void PeerData::updateName(const QString &newName, const QString &newNameOrPhone, asUser()->username = newUsername; asUser()->setNameOrPhone(newNameOrPhone); } else if (isChannel()) { - asChannel()->username = newUsername; + if (asChannel()->username != newUsername) { + asChannel()->username = newUsername; + if (newUsername.isEmpty()) { + asChannel()->flags &= ~MTPDchannel::flag_username; + } else { + asChannel()->flags |= MTPDchannel::flag_username; + } + if (App::main()) { + App::main()->peerUsernameChanged(this); + } + } } Names oldNames = names; @@ -207,7 +217,7 @@ void UserData::setName(const QString &first, const QString &last, const QString firstName = first; lastName = last; } - updateName(lastName.isEmpty() ? firstName : (firstName + ' ' + lastName), phoneName, usern); + updateName(lastName.isEmpty() ? firstName : lng_full_name(lt_first_name, firstName, lt_last_name, lastName), phoneName, usern); } if (updUsername) { if (App::main()) { @@ -383,20 +393,10 @@ void ChannelData::setName(const QString &newName, const QString &usern) { bool updName = !newName.isEmpty(), updUsername = (username != usern); updateName(newName.isEmpty() ? name : newName, QString(), usern); - if (updUsername) { - if (usern.isEmpty()) { - flags &= ~MTPDchannel::flag_username; - } else { - flags |= MTPDchannel::flag_username; - } - if (App::main()) { - App::main()->peerUsernameChanged(this); - } - } } -void ChannelData::updateFull() { - if (!_lastFullUpdate || getms(true) > _lastFullUpdate + UpdateFullChannelTimeout) { +void ChannelData::updateFull(bool force) { + if (!_lastFullUpdate || force || getms(true) > _lastFullUpdate + UpdateFullChannelTimeout) { App::api()->requestFullPeer(this); if (!amCreator() && !inviter) App::api()->requestSelfParticipant(this); } @@ -959,7 +959,7 @@ id(id), type(type), url(url), displayUrl(displayUrl), siteName(siteName), title( void PeerLink::onClick(Qt::MouseButton button) const { if (button == Qt::LeftButton && App::main()) { if (peer() && peer()->isChannel() && App::main()->historyPeer() != peer()) { - if (!peer()->asChannel()->isPublic() && (peer()->asChannel()->isForbidden || peer()->asChannel()->haveLeft() || peer()->asChannel()->wasKicked())) { + if (!peer()->asChannel()->isPublic() && !peer()->asChannel()->amIn()) { App::wnd()->showLayer(new ConfirmBox(lang(lng_channel_not_accessible), true)); } else { App::main()->showPeerHistory(peer()->id, ShowAtUnreadMsgId); diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 6ed1ef8d9..e36a1a1c7 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -451,13 +451,13 @@ private: class ChannelData : public PeerData { public: - ChannelData(const PeerId &id) : PeerData(id), access(0), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))), count(0), date(0), version(0), isForbidden(true), botStatus(-1), inviter(0), _lastFullUpdate(0) { + ChannelData(const PeerId &id) : PeerData(id), access(0), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))), count(1), adminsCount(1), date(0), version(0), isForbidden(true), botStatus(-1), inviter(0), _lastFullUpdate(0) { setName(QString(), QString()); } void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId); void setName(const QString &name, const QString &username); - void updateFull(); + void updateFull(bool force = false); void fullUpdated(); uint64 access; @@ -466,7 +466,7 @@ public: QString username, about; - int32 count; + int32 count, adminsCount; int32 date; int32 version; int32 flags; @@ -491,13 +491,16 @@ public: bool wasKicked() const { return flags & MTPDchannel_flag_was_kicked; } + bool amIn() const { + return !isForbidden && !haveLeft() && !wasKicked(); + } bool canPublish() const { return amCreator() || amEditor(); } - bool amParticipant() const { - return canPublish() || (!haveLeft() && !wasKicked()); - } bool isForbidden; + bool isVerified() const { + return flags & MTPDchannel_flag_is_verified; + } int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other // ImagePtr photoFull;