/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" #include "lang.h" #include "mainwindow.h" #include "mainwidget.h" #include "profilewidget.h" #include "boxes/addcontactbox.h" #include "boxes/confirmbox.h" #include "boxes/photocropbox.h" #include "application.h" #include "boxes/contactsbox.h" #include "ui/filedialog.h" #include "apiwrap.h" #include "window/top_bar_widget.h" ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData *peer) : TWidget(0) , _profile(profile) , _scroll(scroll) , _peer(peer->migrateTo() ? peer->migrateTo() : peer) , _peerUser(_peer->asUser()) , _peerChat(_peer->asChat()) , _peerChannel(_peer->asChannel()) , _migrated(_peer->migrateFrom() ? App::history(_peer->migrateFrom()->id) : 0) , _history(App::history(_peer->id)) , _amCreator(_peerChat ? _peerChat->amCreator() : (_peerChannel ? _peerChannel->amCreator() : false)) , _width(0) , _left(0) , _addToHeight(0) // profile , _nameCache(_peer->name) , _uploadPhoto(this, lang(lng_profile_set_group_photo), st::btnShareContact) , _addParticipant(this, lang(lng_profile_add_participant), st::btnShareContact) , _sendMessage(this, lang(lng_profile_send_message), st::btnShareContact) , _shareContact(this, lang(lng_profile_share_contact), st::btnShareContact) , _inviteToGroup(this, lang(lng_profile_invite_to_group), st::btnShareContact) , _cancelPhoto(this, lang(lng_cancel)) , _createInvitationLink(this, lang(lng_group_invite_create)) , _invitationLink(this, qsl("telegram.me/joinchat/")) , _botSettings(this, lang(lng_profile_bot_settings)) , _botHelp(this, lang(lng_profile_bot_help)) , _pinnedMessage(this, lang(lng_pinned_message)) , _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) : ((_peerChat && _peerChat->adminsEnabled()) ? (_peerChat->admins.size() + 1) : 0)))) // about , _about(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()) , _aboutTop(0) , _aboutHeight(0) , a_photoOver(0) , _a_photo(animation(this, &ProfileInner::step_photo)) , _photoOver(false) // migrate to megagroup , _showMigrate(_peerChat && _amCreator && !_peerChat->isMigrated() && _peerChat->count >= Global::ChatSizeMax()) , _forceShowMigrate(false) , _aboutMigrate(st::normalFont, lang(lng_profile_migrate_about), _defaultOptions, st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()) , _migrate(this, lang(lng_profile_migrate_button), st::btnMigrateToMega) // settings , _enableNotifications(this, lang(lng_profile_enable_notifications)) // shared media , _notAllMediaLoaded(false) // actions , _searchInPeer(this, lang(lng_profile_search_messages)) , _convertToSupergroup(this, lang(lng_profile_convert_button)) , _clearHistory(this, lang(lng_profile_clear_history)) , _deleteConversation(this, lang(_peer->isUser() ? lng_profile_delete_conversation : (_peer->isChat() ? lng_profile_clear_and_exit : (_peer->isMegagroup() ? lng_profile_leave_group : lng_profile_leave_channel)))) , _wasBlocked(_peerUser ? _peerUser->blocked : UserBlockUnknown) , _blockRequest(0) , _blockUser(this, lang((_peerUser && _peerUser->botInfo) ? lng_profile_block_bot : lng_profile_block_user), st::btnRedLink) , _deleteChannel(this, lang(_peer->isMegagroup() ? lng_profile_delete_group : lng_profile_delete_channel), st::btnRedLink) // participants , _pHeight(st::profileListPhotoSize + st::profileListPadding.height() * 2) , _kickWidth(st::linkFont->width(lang(lng_profile_kick))) , _selectedRow(-1) , _lastPreload(0) , _contactId(0) , _kickOver(0) , _kickDown(0) , _kickConfirm(0) , _menu(0) , _updateDelayed(false) { connect(App::wnd(), SIGNAL(imageLoaded()), this, SLOT(update())); connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*))); if (_peerUser) { if (_peerUser->blocked == UserIsBlocked) { _blockUser.setText(lang(_peerUser->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user)); } _phoneText = App::formatPhone(_peerUser->phone.isEmpty() ? App::phoneFromSharedContact(peerToUser(_peerUser->id)) : _peerUser->phone); PhotoData *userPhoto = (_peerUser->photoId && _peerUser->photoId != UnknownPeerPhotoId) ? App::photo(_peerUser->photoId) : 0; if (userPhoto && userPhoto->date) { _photoLink.reset(new PhotoOpenClickHandler(userPhoto, _peer)); } if ((_peerUser->botInfo && !_peerUser->botInfo->inited) || (_peerUser->photoId == UnknownPeerPhotoId) || (_peerUser->photoId && !userPhoto->date) || (_peerUser->blocked == UserBlockUnknown)) { if (App::api()) App::api()->requestFullPeer(_peer); } } else if (_peerChat) { PhotoData *chatPhoto = (_peerChat->photoId && _peerChat->photoId != UnknownPeerPhotoId) ? App::photo(_peerChat->photoId) : 0; if (chatPhoto && chatPhoto->date) { _photoLink.reset(new PhotoOpenClickHandler(chatPhoto, _peer)); } if (_peerChat->photoId == UnknownPeerPhotoId) { if (App::api()) App::api()->requestFullPeer(_peer); } } else if (_peerChannel) { PhotoData *chatPhoto = (_peerChannel->photoId && _peerChannel->photoId != UnknownPeerPhotoId) ? App::photo(_peerChannel->photoId) : 0; if (chatPhoto && chatPhoto->date) { _photoLink.reset(new PhotoOpenClickHandler(chatPhoto, _peer)); } bool needAdmins = (_peerChannel->isMegagroup() && _peerChannel->amEditor()), adminsOutdated = (_peerChannel->isMegagroup() && (_peerChannel->mgInfo->lastParticipantsStatus & MegagroupInfo::LastParticipantsAdminsOutdated)); if (_peerChannel->isMegagroup() && (_peerChannel->mgInfo->lastParticipants.isEmpty() || (needAdmins && adminsOutdated) || _peerChannel->lastParticipantsCountOutdated())) { if (App::api()) App::api()->requestLastParticipants(_peerChannel); } _peerChannel->updateFull(); } // profile _nameText.setText(st::profileNameFont, _nameCache, _textNameOptions); connect(&_uploadPhoto, SIGNAL(clicked()), this, SLOT(onUpdatePhoto())); connect(&_addParticipant, SIGNAL(clicked()), this, SLOT(onAddParticipant())); connect(&_sendMessage, SIGNAL(clicked()), this, SLOT(onSendMessage())); connect(&_shareContact, SIGNAL(clicked()), this, SLOT(onShareContact())); connect(&_inviteToGroup, SIGNAL(clicked()), this, SLOT(onInviteToGroup())); connect(&_cancelPhoto, SIGNAL(clicked()), this, SLOT(onUpdatePhotoCancel())); 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(); if (_peerChat) { QString maxStr = lang(_uploadPhoto.textWidth() > _addParticipant.textWidth() ? lng_profile_set_group_photo : lng_profile_add_participant); _uploadPhoto.setAutoFontSize(st::profileMinBtnPadding, maxStr); _addParticipant.setAutoFontSize(st::profileMinBtnPadding, maxStr); } else if (_peerUser) { QString maxStr; if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) { maxStr = lang(_sendMessage.textWidth() > _inviteToGroup.textWidth() ? lng_profile_send_message : lng_profile_invite_to_group); } else if (!_peerUser->phone.isEmpty()) { maxStr = lang(_sendMessage.textWidth() > _shareContact.textWidth() ? lng_profile_send_message : lng_profile_share_contact); } else { maxStr = lang(lng_profile_send_message); } _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(&_pinnedMessage, SIGNAL(clicked()), this, SLOT(onPinnedMessage())); connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId))); connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId))); connect(App::main(), SIGNAL(peerPhotoChanged(PeerData *)), this, SLOT(peerUpdated(PeerData *))); connect(App::main(), SIGNAL(peerUpdated(PeerData *)), this, SLOT(peerUpdated(PeerData *))); connect(App::main(), SIGNAL(peerNameChanged(PeerData *, const PeerData::Names &, const PeerData::NameFirstChars &)), this, SLOT(peerUpdated(PeerData *))); // about if (_peerUser) { if (!_peerUser->about.isEmpty()) { _about.setText(st::linkFont, _peerUser->about, _peerUser->botInfo ? _historyBotNoMonoOptions : _historyTextNoMonoOptions); } updateBotLinksVisibility(); } else { if (_peerChannel && !_peerChannel->about.isEmpty()) { _about.setText(st::linkFont, _peerChannel->about, _historyTextNoMonoOptions); } _botSettings.hide(); _botHelp.hide(); } updatePinnedMessageVisibility(); // migrate to megagroup connect(&_migrate, SIGNAL(clicked()), this, SLOT(onMigrate())); // settings connect(&_enableNotifications, SIGNAL(clicked()), this, SLOT(onEnableNotifications())); // shared media connect((_mediaButtons[OverviewPhotos] = new LinkButton(this, QString())), SIGNAL(clicked()), this, SLOT(onMediaPhotos())); connect((_mediaButtons[OverviewVideos] = new LinkButton(this, QString())), SIGNAL(clicked()), this, SLOT(onMediaVideos())); connect((_mediaButtons[OverviewMusicFiles] = new LinkButton(this, QString())), SIGNAL(clicked()), this, SLOT(onMediaSongs())); connect((_mediaButtons[OverviewFiles] = new LinkButton(this, QString())), SIGNAL(clicked()), this, SLOT(onMediaDocuments())); connect((_mediaButtons[OverviewVoiceFiles] = new LinkButton(this, QString())), SIGNAL(clicked()), this, SLOT(onMediaAudios())); connect((_mediaButtons[OverviewLinks] = new LinkButton(this, QString())), SIGNAL(clicked()), this, SLOT(onMediaLinks())); updateMediaLinks(); // actions connect(&_searchInPeer, SIGNAL(clicked()), this, SLOT(onSearchInPeer())); connect(&_convertToSupergroup, SIGNAL(clicked()), this, SLOT(onConvertToSupergroup())); connect(&_clearHistory, SIGNAL(clicked()), this, SLOT(onClearHistory())); connect(&_deleteConversation, SIGNAL(clicked()), this, SLOT(onDeleteConversation())); connect(&_blockUser, SIGNAL(clicked()), this, SLOT(onBlockUser())); connect(&_deleteChannel, SIGNAL(clicked()), this, SLOT(onDeleteChannel())); App::contextItem(0); resizeEvent(0); showAll(); } void ProfileInner::onShareContact() { App::main()->shareContactLayer(_peerUser); } void ProfileInner::onInviteToGroup() { Ui::showLayer(new ContactsBox(_peerUser)); } void ProfileInner::onSendMessage() { Ui::showPeerHistory(_peer, ShowAtUnreadMsgId); } void ProfileInner::onSearchInPeer() { App::main()->searchInPeer(_peer); } void ProfileInner::onConvertToSupergroup() { Ui::showLayer(new ConvertToSupergroupBox(_peerChat)); } void ProfileInner::onEnableNotifications() { App::main()->updateNotifySetting(_peer, _enableNotifications.checked() ? NotifySettingSetNotify : NotifySettingSetMuted); } void ProfileInner::saveError(const QString &str) { _errorText = str; resizeEvent(0); showAll(); update(); } void ProfileInner::loadProfilePhotos(int32 yFrom) { _lastPreload = yFrom; int32 yTo = yFrom + (parentWidget() ? parentWidget()->height() : App::wnd()->height()) * 5; MTP::clearLoaderPriorities(); int32 partfrom = _mediaButtons[OverviewVoiceFiles]->y() + _mediaButtons[OverviewVoiceFiles]->height() + st::profileHeaderSkip; yFrom -= partfrom; yTo -= partfrom; if (yTo < 0) return; if (yFrom < 0) yFrom = 0; yFrom /= _pHeight; yTo = yTo / _pHeight + 1; if (yFrom >= _participants.size()) return; if (yTo > _participants.size()) yTo = _participants.size(); for (int32 i = yFrom; i < yTo; ++i) { _participants[i]->loadUserpic(); } } void ProfileInner::onUpdatePhoto() { saveError(); QStringList imgExtensions(cImgExtensions()); QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;All files (*.*)")); QImage img; QString file; QByteArray remoteContent; if (filedialogGetOpenFile(file, remoteContent, lang(lng_choose_images), filter)) { if (!remoteContent.isEmpty()) { img = App::readImage(remoteContent); } else { if (!file.isEmpty()) { img = App::readImage(file); } } } else { return; } if (img.isNull() || img.width() > 10 * img.height() || img.height() > 10 * img.width()) { saveError(lang(lng_bad_photo)); return; } PhotoCropBox *box = new PhotoCropBox(img, _peer); connect(box, SIGNAL(closed()), this, SLOT(onPhotoUpdateStart())); Ui::showLayer(box); } void ProfileInner::onClearHistory() { if (_peerChannel) return; ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : lng_sure_delete_group_history(lt_group, _peer->name), lang(lng_box_delete), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onClearHistorySure())); Ui::showLayer(box); } void ProfileInner::onClearHistorySure() { Ui::hideLayer(); App::main()->clearHistory(_peer); } void ProfileInner::onDeleteConversation() { ConfirmBox *box = new ConfirmBox(_peer->isUser() ? lng_sure_delete_history(lt_contact, _peer->name) : (_peer->isChat() ? lng_sure_delete_and_exit(lt_group, _peer->name) : lang(_peer->isMegagroup() ? lng_sure_leave_group : lng_sure_leave_channel)), lang(_peer->isUser() ? lng_box_delete : lng_box_leave), _peer->isChannel() ? st::defaultBoxButton : st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteConversationSure())); Ui::showLayer(box); } void ProfileInner::onDeleteConversationSure() { Ui::hideLayer(); if (_peerUser) { App::main()->deleteConversation(_peer); } else if (_peerChat) { Ui::showChatsList(); MTP::send(MTPmessages_DeleteChatUser(_peerChat->inputChat, App::self()->inputUser), App::main()->rpcDone(&MainWidget::deleteHistoryAfterLeave, _peer), App::main()->rpcFail(&MainWidget::leaveChatFailed, _peer)); } else if (_peerChannel) { Ui::showChatsList(); if (_peerChannel->migrateFrom()) { App::main()->deleteConversation(_peerChannel->migrateFrom()); } MTP::send(MTPchannels_LeaveChannel(_peerChannel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); } } void ProfileInner::onDeleteChannel() { if (!_peerChannel) return; ConfirmBox *box = new ConfirmBox(lang(_peer->isMegagroup() ? lng_sure_delete_group : lng_sure_delete_channel), lang(lng_box_delete), st::attentionBoxButton); connect(box, SIGNAL(confirmed()), this, SLOT(onDeleteChannelSure())); Ui::showLayer(box); } void ProfileInner::onDeleteChannelSure() { if (_peerChannel) { Ui::hideLayer(); Ui::showChatsList(); if (_peerChannel->migrateFrom()) { App::main()->deleteConversation(_peerChannel->migrateFrom()); } MTP::send(MTPchannels_DeleteChannel(_peerChannel->inputChannel), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::deleteChannelFailed)); } } void ProfileInner::onBlockUser() { if (!_peerUser || _blockRequest) return; if (_peerUser->blocked == UserIsBlocked) { _blockRequest = MTP::send(MTPcontacts_Unblock(_peerUser->inputUser), rpcDone(&ProfileInner::blockDone, false), rpcFail(&ProfileInner::blockFail)); } else { _blockRequest = MTP::send(MTPcontacts_Block(_peerUser->inputUser), rpcDone(&ProfileInner::blockDone, true), rpcFail(&ProfileInner::blockFail)); } } void ProfileInner::blockDone(bool blocked, const MTPBool &result) { _blockRequest = 0; if (!_peerUser) return; _peerUser->blocked = blocked ? UserIsBlocked : UserIsNotBlocked; emit App::main()->peerUpdated(_peerUser); } bool ProfileInner::blockFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; _blockRequest = 0; return false; } void ProfileInner::onAddParticipant() { if (_peerChat) { Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent)); } else if (_peerChannel && _peerChannel->mgInfo) { MembersAlreadyIn already; for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) { already.insert(*i, true); } Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already)); } } void ProfileInner::onMigrate() { if (!_peerChat) return; ConfirmBox *box = new ConfirmBox(lang(lng_profile_migrate_sure)); connect(box, SIGNAL(confirmed()), this, SLOT(onMigrateSure())); Ui::showLayer(box); } void ProfileInner::onMigrateSure() { if (!_peerChat) return; MTP::send(MTPmessages_MigrateChat(_peerChat->inputChat), rpcDone(&ProfileInner::migrateDone), rpcFail(&ProfileInner::migrateFail)); } void ProfileInner::onUpdatePhotoCancel() { App::app()->cancelPhotoUpdate(_peer->id); showAll(); update(); } void ProfileInner::onPhotoUpdateStart() { showAll(); update(); } void ProfileInner::onPhotoUpdateFail(PeerId peer) { if (_peer->id != peer) return; saveError(lang(lng_bad_photo)); showAll(); update(); } void ProfileInner::onPhotoUpdateDone(PeerId peer) { if (_peer->id != peer) return; saveError(); showAll(); update(); } void ProfileInner::onMediaPhotos() { App::main()->showMediaOverview(_peer, OverviewPhotos); } void ProfileInner::onMediaVideos() { App::main()->showMediaOverview(_peer, OverviewVideos); } void ProfileInner::onMediaSongs() { App::main()->showMediaOverview(_peer, OverviewMusicFiles); } void ProfileInner::onMediaDocuments() { App::main()->showMediaOverview(_peer, OverviewFiles); } void ProfileInner::onMediaAudios() { App::main()->showMediaOverview(_peer, OverviewVoiceFiles); } void ProfileInner::onMediaLinks() { App::main()->showMediaOverview(_peer, OverviewLinks); } void ProfileInner::onInvitationLink() { if (!_peerChat && !_peerChannel) return; QApplication::clipboard()->setText(_peerChat ? _peerChat->invitationUrl : (_peerChannel ? _peerChannel->invitationUrl : QString())); Ui::showLayer(new InformBox(lang(lng_group_invite_copied))); } void ProfileInner::onPublicLink() { if (!_peerChannel) return; if (_peerChannel->isPublic()) { QApplication::clipboard()->setText(qsl("https://telegram.me/") + _peerChannel->username); Ui::showLayer(new InformBox(lang(lng_channel_public_link_copied))); } else { Ui::showLayer(new SetupChannelBox(_peerChannel, true)); } } void ProfileInner::onMembers() { if (!_peerChannel) return; Ui::showLayer(new MembersBox(_peerChannel, MembersFilterRecent)); } void ProfileInner::onAdmins() { if (_peerChannel) { Ui::showLayer(new MembersBox(_peerChannel, MembersFilterAdmins)); } else if (_peerChat) { Ui::showLayer(new ContactsBox(_peerChat, MembersFilterAdmins)); } } void ProfileInner::onCreateInvitationLink() { if (!_peerChat && !_peerChannel) return; ConfirmBox *box = new ConfirmBox(lang(((_peerChat && _peerChat->invitationUrl.isEmpty()) || (_peerChannel && _peerChannel->invitationUrl.isEmpty())) ? lng_group_invite_about : lng_group_invite_about_new)); connect(box, SIGNAL(confirmed()), this, SLOT(onCreateInvitationLinkSure())); Ui::showLayer(box); } void ProfileInner::onCreateInvitationLinkSure() { if (!_peerChat && !_peerChannel) return; if (_peerChat) { MTP::send(MTPmessages_ExportChatInvite(_peerChat->inputChat), rpcDone(&ProfileInner::chatInviteDone)); } else if (_peerChannel) { MTP::send(MTPchannels_ExportInvite(_peerChannel->inputChannel), rpcDone(&ProfileInner::chatInviteDone)); } } void ProfileInner::chatInviteDone(const MTPExportedChatInvite &result) { if (!_peerChat && !_peerChannel) return; if (_peerChat) { _peerChat->invitationUrl = (result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString(); } else { _peerChannel->invitationUrl = (result.type() == mtpc_chatInviteExported) ? qs(result.c_chatInviteExported().vlink) : QString(); } updateInvitationLink(); showAll(); resizeEvent(0); Ui::hideLayer(); } void ProfileInner::onFullPeerUpdated(PeerData *peer) { if (peer != _peer) return; if (_peerUser) { PhotoData *userPhoto = (_peerUser->photoId && _peerUser->photoId != UnknownPeerPhotoId) ? App::photo(_peerUser->photoId) : 0; if (userPhoto && userPhoto->date) { _photoLink.reset(new PhotoOpenClickHandler(userPhoto, _peer)); } else { _photoLink.clear(); } if (_peerUser) { if (_peerUser->about.isEmpty()) { _about = Text(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()); } else { _about.setText(st::linkFont, _peerUser->about, _peerUser->botInfo ? _historyBotNoMonoOptions : _historyTextNoMonoOptions); } updateBotLinksVisibility(); resizeEvent(0); } } else if (_peerChat) { updateInvitationLink(); _showMigrate = (_peerChat && _amCreator && !_peerChat->isMigrated() && (_forceShowMigrate || _peerChat->count >= Global::ChatSizeMax())); showAll(); resizeEvent(0); _admins.setText(lng_channel_admins_link(lt_count, _peerChat->adminsEnabled() ? (_peerChat->admins.size() + 1) : 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 = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); if (_peerChannel->about.isEmpty()) { _about = Text(st::wndMinWidth - st::profilePadding.left() - st::profilePadding.right()); } else { _about.setText(st::linkFont, _peerChannel->about, _historyTextNoMonoOptions); } showAll(); resizeEvent(0); } } void ProfileInner::onBotSettings() { if (!_peerUser || !_peerUser->botInfo) return; for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) { QString cmd = _peerUser->botInfo->commands.at(i).command; if (!cmd.compare(qsl("settings"), Qt::CaseInsensitive)) { Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); App::sendBotCommand(_peerUser, '/' + cmd); return; } } updateBotLinksVisibility(); } void ProfileInner::onBotHelp() { if (!_peerUser || !_peerUser->botInfo) return; for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) { QString cmd = _peerUser->botInfo->commands.at(i).command; if (!cmd.compare(qsl("help"), Qt::CaseInsensitive)) { Ui::showPeerHistory(_peer, ShowAtTheEndMsgId); App::sendBotCommand(_peerUser, '/' + cmd); return; } } updateBotLinksVisibility(); } void ProfileInner::onPinnedMessage() { if (!_peerChannel || !_peerChannel->isMegagroup() || !_peerChannel->mgInfo->pinnedMsgId) { updatePinnedMessageVisibility(); return; } Ui::showPeerHistory(_peer, _peerChannel->mgInfo->pinnedMsgId); } void ProfileInner::peerUpdated(PeerData *data) { if (data == _peer) { PhotoData *photo = 0; if (_peerUser) { _phoneText = App::formatPhone(_peerUser->phone.isEmpty() ? App::phoneFromSharedContact(peerToUser(_peerUser->id)) : _peerUser->phone); if (_peerUser->photoId && _peerUser->photoId != UnknownPeerPhotoId) photo = App::photo(_peerUser->photoId); if (_wasBlocked != _peerUser->blocked) { _wasBlocked = _peerUser->blocked; _blockUser.setText(lang((_peerUser->blocked == UserIsBlocked) ? (_peerUser->botInfo ? lng_profile_unblock_bot : lng_profile_unblock_user) : (_peerUser->botInfo ? lng_profile_block_bot : lng_profile_block_user))); } } else if (_peerChat) { if (_peerChat->photoId && _peerChat->photoId != UnknownPeerPhotoId) photo = App::photo(_peerChat->photoId); _admins.setText(lng_channel_admins_link(lt_count, _peerChat->adminsEnabled() ? (_peerChat->admins.size() + 1) : 0)); _showMigrate = (_peerChat && _amCreator && !_peerChat->isMigrated() && (_forceShowMigrate || _peerChat->count >= Global::ChatSizeMax())); if (App::main()) App::main()->topBar()->showAll(); } else if (_peerChannel) { if (_peerChannel->photoId && _peerChannel->photoId != UnknownPeerPhotoId) photo = App::photo(_peerChannel->photoId); 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 = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); updatePinnedMessageVisibility(); } if (photo && photo->date) { _photoLink.reset(new PhotoOpenClickHandler(photo, _peer)); } else { _photoLink.clear(); } if (_peer->name != _nameCache) { _nameCache = _peer->name; _nameText.setText(st::profileNameFont, _nameCache, _textNameOptions); } } if (!_updateDelayed) { _updateDelayed = true; QMetaObject::invokeMethod(this, "onUpdateDelayed", Qt::QueuedConnection); } } void ProfileInner::onUpdateDelayed() { _updateDelayed = false; showAll(); resizeEvent(0); update(); } void ProfileInner::updateOnlineDisplay() { reorderParticipants(); update(); } void ProfileInner::updateOnlineDisplayTimer() { int32 t = unixtime(), minIn = 86400; if (_peerUser) { minIn = App::onlineWillChangeIn(_peerUser, t); } else if (_peerChat) { if (_peerChat->participants.isEmpty()) return; for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) { int32 onlineWillChangeIn = App::onlineWillChangeIn(i.key(), t); if (onlineWillChangeIn < minIn) { minIn = onlineWillChangeIn; } } } else if (_peerChannel) { } App::main()->updateOnlineDisplayIn(minIn * 1000); } void ProfileInner::reorderParticipants() { int32 was = _participants.size(), t = unixtime(), onlineCount = 0; if (_peerChat && _peerChat->amIn()) { if (!_peerChat->participants.isEmpty()) { _participants.clear(); for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) { if (*i) { delete *i; *i = 0; } } _participants.reserve(_peerChat->participants.size()); _participantsData.resize(_peerChat->participants.size()); } UserData *self = App::self(); bool onlyMe = true; for (ChatData::Participants::const_iterator i = _peerChat->participants.cbegin(), e = _peerChat->participants.cend(); i != e; ++i) { UserData *user = i.key(); int32 until = App::onlineForSort(user, t); Participants::iterator before = _participants.begin(); if (user != self) { if (before != _participants.end() && (*before) == self) { ++before; } while (before != _participants.end() && App::onlineForSort(*before, t) >= until) { ++before; } if (until > t && onlyMe) onlyMe = false; } _participants.insert(before, user); if (until > t) { ++onlineCount; } } if (_peerChat->noParticipantInfo()) { if (App::api()) App::api()->requestFullPeer(_peer); if (_onlineText.isEmpty()) _onlineText = lng_chat_status_members(lt_count, _peerChat->count); } else if (onlineCount && !onlyMe) { _onlineText = lng_chat_status_members_online(lt_count, _participants.size(), lt_count_online, onlineCount); } else { _onlineText = lng_chat_status_members(lt_count, _participants.size()); } loadProfilePhotos(_lastPreload); } else if (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->amIn() && !_peerChannel->mgInfo->lastParticipants.isEmpty()) { bool needAdmins = true, adminsOutdated = (_peerChannel->mgInfo->lastParticipantsStatus & MegagroupInfo::LastParticipantsAdminsOutdated); bool orderByOnline = (_peerChannel->count > 0) && (_peerChannel->count <= Global::ChatSizeMax()); _onlineText.clear(); if (_peerChannel->mgInfo->lastParticipants.isEmpty() || (needAdmins && adminsOutdated) || _peerChannel->lastParticipantsCountOutdated()) { if (App::api()) App::api()->requestLastParticipants(_peerChannel); } else if (!_peerChannel->mgInfo->lastParticipants.isEmpty()) { const MegagroupInfo::LastParticipants &list(_peerChannel->mgInfo->lastParticipants); int32 s = list.size(); if (orderByOnline) { _participants.clear(); for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) { if (*i) { delete *i; *i = 0; } } _participants.reserve(s); UserData *self = App::self(); bool onlyMe = true; for (int32 i = 0; i < s; ++i) { UserData *user = list.at(i); int32 until = App::onlineForSort(user, t); Participants::iterator before = _participants.begin(); if (user != self) { if (before != _participants.end() && (*before) == self) { ++before; } while (before != _participants.end() && App::onlineForSort(*before, t) >= until) { ++before; } if (until > t && onlyMe) onlyMe = false; } _participants.insert(before, user); if (until > t) { ++onlineCount; } } if (onlineCount && !onlyMe) { _onlineText = lng_chat_status_members_online(lt_count, _peerChannel->count, lt_count_online, onlineCount); } else { _onlineText = lng_chat_status_members(lt_count, _peerChannel->count); } } else { for (int32 i = 0, l = _participants.size(); i < l; ++i) { if (i >= s || _participants.at(i) != list.at(i)) { if (_participantsData.at(i)) { delete _participantsData.at(i); _participantsData[i] = 0; } if (i < s) { _participants[i] = list.at(i); } } } if (_participants.size() > s) { _participants.resize(s); } else { _participants.reserve(s); for (int32 i = _participants.size(); i < s; ++i) { _participants.push_back(list.at(i)); } } } _participantsData.resize(s); } if (_onlineText.isEmpty()) { _onlineText = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); } loadProfilePhotos(_lastPreload); } else { _participants.clear(); if (_peerUser) { _onlineText = App::onlineText(_peerUser, t, true); } else if (_peerChannel) { _onlineText = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status); } else { _onlineText = lang(lng_chat_status_unaccessible); } } if (was != _participants.size()) { resizeEvent(0); } } void ProfileInner::start() { } void ProfileInner::peerUsernameChanged() { if (_peerChannel) { _username.setText(_peerChannel->isPublic() ? (qsl("telegram.me/") + _peerChannel->username) : lang(lng_profile_create_public_link)); resizeEvent(0); showAll(); } update(); } bool ProfileInner::event(QEvent *e) { if (e->type() == QEvent::MouseMove) { _lastPos = static_cast(e)->globalPos(); updateSelected(); } return QWidget::event(e); } void ProfileInner::paintEvent(QPaintEvent *e) { if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; Painter p(this); QRect r(e->rect()); p.setClipRect(r); int32 top = 0, l_time = unixtime(); // profile top += st::profilePadding.top(); if (_photoLink || _peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator)) { _peer->paintUserpic(p, st::profilePhotoSize, _left, top); } else { if (a_photoOver.current() < 1) { p.drawPixmap(QPoint(_left, top), App::sprite(), st::setPhotoImg); } if (a_photoOver.current() > 0) { p.setOpacity(a_photoOver.current()); p.drawPixmap(QPoint(_left, top), App::sprite(), st::setOverPhotoImg); p.setOpacity(1); } } int32 namew = _width - st::profilePhotoSize - st::profileNameLeft; p.setPen(st::black->p); if (_peer->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; if (_peerUser && !_peerUser->username.isEmpty()) { 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() || _peerChannel->canEditUsername())) { addbyname = st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); } if (!_peerChannel || !_peerChannel->canViewParticipants() || _peerChannel->isMegagroup()) { 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() + 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() + st::profilePhoneFont->ascent, _errorText); } if (!_phoneText.isEmpty()) { p.setPen(st::black->p); p.setFont(st::linkFont->f); p.drawText(_left + st::profilePhotoSize + st::profilePhoneLeft, top + addbyname + st::profilePhoneTop + st::profilePhoneFont->ascent, _phoneText); } top += st::profilePhotoSize; top += st::profileButtonTop; if ((!_peerChat || _peerChat->canEdit()) && (!_peerChannel || _amCreator || (_peerChannel->canAddParticipants() && _peerChannel->isMegagroup()))) { top += _shareContact.height(); } else { top -= st::profileButtonTop; } // 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(_peerChannel ? lng_profile_description_section : lng_profile_about_section)); top += st::profileHeaderSkip; _about.draw(p, _left, top, _width); top += _aboutHeight; } // migrate to megagroup if (_showMigrate) { p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); top += st::profileHeaderSkip; _aboutMigrate.draw(p, _left, top, _width); top += _aboutMigrate.countHeight(_width) + st::setLittleSkip; p.setFont(st::normalFont); p.setPen(st::black); p.drawText(_left, top + st::normalFont->ascent, lng_profile_migrate_feature1(lt_count, Global::MegagroupSizeMax())); top += st::normalFont->height + st::setLittleSkip; p.drawText(_left, top + st::normalFont->ascent, lang(lng_profile_migrate_feature2)); top += st::normalFont->height + st::setLittleSkip; p.drawText(_left, top + st::normalFont->ascent, lang(lng_profile_migrate_feature3)); top += st::normalFont->height + st::setLittleSkip; p.drawText(_left, top + st::normalFont->ascent, lang(lng_profile_migrate_feature4)); top += st::normalFont->height + st::setSectionSkip; top += _migrate.height(); } // settings p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_settings_section)); top += st::profileHeaderSkip; // invite link stuff if (_amCreator && ((_peerChat && _peerChat->canEdit()) || (_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 p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_shared_media)); top += st::profileHeaderSkip; p.setFont(st::linkFont->f); p.setPen(st::black->p); bool mediaFound = false; for (int i = 0; i < OverviewCount; ++i) { if (!_mediaButtons[i]->isHidden()) { mediaFound = true; top += _mediaButtons[i]->height() + st::setLittleSkip; } } if (_notAllMediaLoaded || !mediaFound) { p.drawText(_left, top + st::linkFont->ascent, lang(_notAllMediaLoaded ? lng_profile_loading : lng_profile_no_media)); top += _mediaButtons[OverviewPhotos]->height(); } else { top -= st::setLittleSkip; } // actions p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, lang(lng_profile_actions_section)); top += st::profileHeaderSkip; top += _searchInPeer.height() + st::setLittleSkip; if (_peerChat && _amCreator && !_showMigrate) { top += _convertToSupergroup.height() + st::setLittleSkip; } if (_peerUser || _peerChat) { top += _clearHistory.height() + st::setLittleSkip; } if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { top += _deleteConversation.height(); } if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) { top += st::setSectionSkip + _blockUser.height(); } else if (canDeleteChannel()) { top += (_peerChannel->isMegagroup() ? 0 : (st::setSectionSkip - st::setLittleSkip)) + _deleteChannel.height(); } // participants if ((_peerChat && _peerChat->amIn()) || (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->amIn())) { QString sectionHeader = lang(_participants.isEmpty() ? lng_profile_loading : lng_profile_participants_section); p.setFont(st::profileHeaderFont->f); p.setPen(st::profileHeaderColor->p); p.drawText(_left + st::profileHeaderLeft, top + st::profileHeaderTop + st::profileHeaderFont->ascent, sectionHeader); top += st::profileHeaderSkip; int32 partfrom = top; if (!_participants.isEmpty()) { if (App::self()) { if (_peerChat) { if (_peerChat->amAdmin() && _peerChat->admins.constFind(App::self()) == _peerChat->admins.cend()) { _peerChat->admins.insert(App::self()); } else if (!_peerChat->amAdmin() && _peerChat->admins.constFind(App::self()) != _peerChat->admins.cend()) { _peerChat->admins.remove(App::self()); } } else if (_peerChannel && _peerChannel->isMegagroup()) { if ((_peerChannel->amCreator() || _peerChannel->amEditor()) && _peerChannel->mgInfo->lastAdmins.constFind(App::self()) == _peerChannel->mgInfo->lastAdmins.cend()) { _peerChannel->mgInfo->lastAdmins.insert(App::self()); } else if (!_peerChannel->amCreator() && !_peerChannel->amEditor() && _peerChannel->mgInfo->lastAdmins.constFind(App::self()) != _peerChannel->mgInfo->lastAdmins.cend()) { _peerChannel->mgInfo->lastAdmins.remove(App::self()); } } } int32 cnt = 0, fullCnt = _participants.size(); for (Participants::const_iterator i = _participants.cbegin(), e = _participants.cend(); i != e; ++i, ++cnt) { int32 top = partfrom + cnt * _pHeight; if (top + _pHeight <= r.top()) continue; if (top >= r.y() + r.height()) break; if (_selectedRow == cnt) { p.fillRect(_left - st::profileListPadding.width(), top, _width + 2 * st::profileListPadding.width(), _pHeight, st::profileHoverBG->b); } UserData *user = *i; user->paintUserpic(p, st::profileListPhotoSize, _left, top + st::profileListPadding.height()); ParticipantData *data = _participantsData[cnt]; if (!data) { data = _participantsData[cnt] = new ParticipantData(); data->name.setText(st::profileListNameFont, user->name, _textNameOptions); if (user->botInfo) { if (user->botInfo->readsAllHistory) { data->online = lang(lng_status_bot_reads_all); } else { data->online = lang(lng_status_bot_not_reads_all); } } else { data->online = App::onlineText(user, l_time); } if (_peerChat) { data->admin = (peerFromUser(_peerChat->creator) == user->id) || (_peerChat->adminsEnabled() && (_peerChat->admins.constFind(user) != _peerChat->admins.cend())); } else if (_peerChannel) { data->admin = (_peerChannel->mgInfo->lastAdmins.constFind(user) != _peerChannel->mgInfo->lastAdmins.cend()); } else { data->admin = false; } if (_amCreator) { data->cankick = (user != App::self()); } else if (_peerChat && _peerChat->amAdmin()) { data->cankick = (user != App::self()) && !data->admin; } else if (_peerChannel && _peerChannel->amEditor()) { data->cankick = (user != App::self()) && !data->admin; } else { data->cankick = (user != App::self()) && !_peerChannel && (_peerChat->invitedByMe.constFind(user) != _peerChat->invitedByMe.cend()); } } p.setPen(st::profileListNameColor); p.setFont(st::linkFont); data->name.drawElided(p, _left + st::profileListPhotoSize + st::profileListPadding.width(), top + st::profileListNameTop, _width - _kickWidth - st::profileListPadding.width() - st::profileListPhotoSize - st::profileListPadding.width()); p.setFont(st::profileSubFont); p.setPen(App::onlineColorUse(user, l_time) ? st::profileOnlineColor : st::profileOfflineColor); p.drawText(_left + st::profileListPhotoSize + st::profileListPadding.width(), top + st::profileListPadding.height() + st::profileListPhotoSize - st::profileListStatusBottom, data->online); if (_selectedRow == cnt && data->cankick) { bool over = (user == _kickOver && (!_kickDown || _kickDown == _kickOver)); p.setFont((over ? st::linkOverFont : st::linkFont)->f); if (user == _kickOver && _kickOver == _kickDown) { p.setPen(st::btnDefLink.downColor->p); } else { p.setPen(st::btnDefLink.color->p); } p.drawTextRight(width() - _left - _width, top + st::profileListNameTop, width(), lang(lng_profile_kick), _kickWidth); } else if (data->admin) { p.setFont(st::profileSubFont); p.setPen(st::profileOfflineColor); p.drawTextRight(width() - _left - _width, top + st::profileListNameTop, width(), lang(lng_profile_admin)); } } top += fullCnt * _pHeight; } } top += st::profileHeaderTop + st::profileHeaderFont->ascent - st::linkFont->ascent; top += _clearHistory.height(); } void ProfileInner::mouseMoveEvent(QMouseEvent *e) { _lastPos = e->globalPos(); updateSelected(); bool photoOver = QRect(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize).contains(e->pos()); if (photoOver != _photoOver) { _photoOver = photoOver; if (!_photoLink && ((_peerChat && _peerChat->canEdit()) || (_peerChannel && _amCreator))) { a_photoOver.start(_photoOver ? 1 : 0); _a_photo.start(); } } if (!_photoLink && (_peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator))) { setCursor((_kickOver || _kickDown || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } else { setCursor((_kickOver || _kickDown || _photoOver || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } } void ProfileInner::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { update(QRect(_left, _aboutTop, _width, _aboutHeight)); } void ProfileInner::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { update(QRect(_left, _aboutTop, _width, _aboutHeight)); } void ProfileInner::updateSelected() { if (!isVisible()) return; QPoint lp = mapFromGlobal(_lastPos); ClickHandlerPtr lnk; ClickHandlerHost *lnkhost = nullptr; if (!_about.isEmpty() && lp.y() >= _aboutTop && lp.y() < _aboutTop + _aboutHeight && lp.x() >= _left && lp.x() < _left + _width) { auto textState = _about.getState(lp.x() - _left, lp.y() - _aboutTop, _width); lnk = textState.link; lnkhost = this; } ClickHandler::setActive(lnk, lnkhost); int32 participantsTop = 0; if (canDeleteChannel()) { participantsTop = _deleteChannel.y() + _deleteChannel.height(); } else if (_peerChannel && _amCreator) { participantsTop = _searchInPeer.y() + _searchInPeer.height(); } else { participantsTop = _deleteConversation.y() + _deleteConversation.height(); } participantsTop += st::profileHeaderSkip; int32 newSelected = (lp.x() >= _left - st::profileListPadding.width() && lp.x() < _left + _width + st::profileListPadding.width() && lp.y() >= participantsTop) ? (lp.y() - participantsTop) / _pHeight : -1; UserData *newKickOver = 0; if (newSelected >= 0 && newSelected < _participants.size()) { ParticipantData *data = _participantsData[newSelected]; if (data && data->cankick) { int32 top = participantsTop + newSelected * _pHeight + st::profileListNameTop; if ((lp.x() >= _left + _width - _kickWidth) && (lp.x() < _left + _width) && (lp.y() >= top) && (lp.y() < top + st::linkFont->height)) { newKickOver = _participants[newSelected]; } } } if (_kickOver != newKickOver) { _kickOver = newKickOver; update(); } if (_kickDown) return; if (newSelected != _selectedRow) { _selectedRow = newSelected; update(); } } void ProfileInner::mousePressEvent(QMouseEvent *e) { _lastPos = e->globalPos(); updateSelected(); ClickHandler::pressed(); if (e->button() == Qt::LeftButton) { if (_kickOver) { _kickDown = _kickOver; update(); } else if (_selectedRow >= 0 && _selectedRow < _participants.size()) { App::main()->showPeerProfile(_participants[_selectedRow]); } else if (QRect(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize).contains(e->pos())) { if (_photoLink) { _photoLink->onClick(e->button()); } else if ((_peerChat && _peerChat->canEdit()) || (_peerChannel && _amCreator)) { onUpdatePhoto(); } } } } void ProfileInner::mouseReleaseEvent(QMouseEvent *e) { _lastPos = e->globalPos(); updateSelected(); if (_kickDown && _kickDown == _kickOver) { _kickConfirm = _kickOver; ConfirmBox *box = new ConfirmBox(lng_profile_sure_kick(lt_user, _kickOver->firstName), lang(lng_box_remove)); connect(box, SIGNAL(confirmed()), this, SLOT(onKickConfirm())); Ui::showLayer(box); } _kickDown = 0; if (!_photoLink && (_peerUser || (_peerChat && !_peerChat->canEdit()) || (_peerChannel && !_amCreator))) { setCursor((_kickOver || _kickDown || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } else { setCursor((_kickOver || _kickDown || _photoOver || ClickHandler::getActive()) ? style::cur_pointer : style::cur_default); } update(); if (ClickHandlerPtr activated = ClickHandler::unpressed()) { App::activateClickHandler(activated, e->button()); } } void ProfileInner::onKickConfirm() { if (_peerChat) { App::main()->kickParticipant(_peerChat, _kickConfirm); } else if (_peerChannel) { Ui::hideLayer(); App::api()->kickParticipant(_peerChannel, _kickConfirm); } } void ProfileInner::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape || e->key() == Qt::Key_Back) { App::main()->showBackFromStack(); } _secretText += e->text().toLower(); int32 size = _secretText.size(), from = 0; while (size > from) { QStringRef str(_secretText.midRef(from)); if (str == qstr("tosupergroup")) { _forceShowMigrate = true; peerUpdated(_peer); } else if (qsl("tosupergroup").startsWith(str)) { break; } ++from; } _secretText = (size > from) ? _secretText.mid(from) : QString(); } void ProfileInner::enterEvent(QEvent *e) { setMouseTracking(true); _lastPos = QCursor::pos(); updateSelected(); return TWidget::enterEvent(e); } void ProfileInner::leaveEvent(QEvent *e) { setMouseTracking(false); _lastPos = QCursor::pos(); updateSelected(); return TWidget::leaveEvent(e); } void ProfileInner::leaveToChildEvent(QEvent *e) { _lastPos = QCursor::pos(); updateSelected(); return TWidget::leaveToChildEvent(e); } bool ProfileInner::updateMediaLinks(int32 *addToScroll) { QPoint p(addToScroll ? mapFromGlobal(QCursor::pos()) : QPoint(0, 0)); bool oneWasShown = false; for (int i = 0; i < OverviewCount; ++i) { if (!_mediaButtons[i]->isHidden()) { oneWasShown = true; break; } } bool newNotAllMediaLoaded = false, changed = false, substracted = !_notAllMediaLoaded && oneWasShown; bool notAllHistoryLoaded = false, notAllMigratedLoaded = false; bool oneIsShown = false; int32 y = _mediaButtons[OverviewPhotos]->y(); if (addToScroll) *addToScroll = 0; for (int i = 0; i < OverviewCount; ++i) { int32 addToY = _mediaButtons[i]->height() + st::setLittleSkip; int32 count = _history->overviewCount(i), additional = _migrated ? _migrated->overviewCount(i) : 0; int32 sum = (count > 0 ? count : 0) + (additional > 0 ? additional : 0); if (sum > 0) { _mediaButtons[i]->setText(overviewLinkText(i, sum)); if (_mediaButtons[i]->isHidden()) { _mediaButtons[i]->show(); changed = true; if (addToScroll && p.y() >= y) { p.setY(p.y() + addToY); *addToScroll += addToY; } } y += addToY; oneIsShown = true; } else { if (!_mediaButtons[i]->isHidden()) { _mediaButtons[i]->hide(); changed = true; if (addToScroll && p.y() >= y + addToY) { p.setY(p.y() - addToY); *addToScroll -= addToY; } } if (count < 0) { notAllHistoryLoaded = true; } if (additional < 0) { notAllMigratedLoaded = true; } } } newNotAllMediaLoaded = notAllHistoryLoaded || notAllMigratedLoaded; if (newNotAllMediaLoaded != _notAllMediaLoaded) { _notAllMediaLoaded = newNotAllMediaLoaded; changed = true; int32 addToY = _mediaButtons[OverviewPhotos]->height(); if (_notAllMediaLoaded) { if (addToScroll && p.y() >= y) { p.setY(p.y() + addToY); *addToScroll += addToY; } } else { if (addToScroll && p.y() >= y + addToY) { p.setY(p.y() - addToY); *addToScroll -= addToY; } } if (App::main()) { if (notAllHistoryLoaded) App::main()->preloadOverviews(_peer); if (notAllMigratedLoaded) App::main()->preloadOverviews(_migrated->peer); } } bool newSubstracted = !_notAllMediaLoaded && oneIsShown; if (newSubstracted && newSubstracted != substracted) { int32 addToY = st::setLittleSkip; if (addToScroll && p.y() >= y + addToY) { p.setY(p.y() - addToY); *addToScroll -= addToY; } } return changed; } void ProfileInner::migrateDone(const MTPUpdates &updates) { Ui::hideLayer(); App::main()->sentUpdatesReceived(updates); const QVector *v = 0; switch (updates.type()) { case mtpc_updates: v = &updates.c_updates().vchats.c_vector().v; break; case mtpc_updatesCombined: v = &updates.c_updatesCombined().vchats.c_vector().v; break; default: LOG(("API Error: unexpected update cons %1 (ProfileInner::migrateDone)").arg(updates.type())); break; } PeerData *peer = 0; if (v && !v->isEmpty()) { for (int32 i = 0, l = v->size(); i < l; ++i) { if (v->at(i).type() == mtpc_channel) { peer = App::channel(v->at(i).c_channel().vid.v); Ui::showPeerHistory(peer, ShowAtUnreadMsgId); QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount())); } } } if (!peer) { LOG(("API Error: channel not found in updates (ProfileInner::migrateDone)")); } } bool ProfileInner::migrateFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; Ui::hideLayer(); return true; } bool ProfileInner::canDeleteChannel() const { return _peerChannel && _amCreator && (_peerChannel->count <= 1000); } void ProfileInner::resizeEvent(QResizeEvent *e) { _width = qMin(width() - st::profilePadding.left() - st::profilePadding.right(), int(st::profileMaxWidth)); _left = (width() - _width) / 2; int32 top = 0, btnWidth = (_width - st::profileButtonSkip) / 2; // profile top += st::profilePadding.top(); int32 addbyname = 0; if (_peerChannel && (_peerChannel->isPublic() || _peerChannel->canEditUsername())) { _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); if (!_admins.isHidden()) { _admins.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); addbyname += st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent); } if ((_peerChat && _amCreator && _peerChat->canEdit()) || (_peerChannel && (_amCreator || _peerChannel->amEditor() || _peerChannel->amModerator()))) { _cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhotoSize - st::linkFont->height); } else { _cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhoneTop); _botSettings.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent) + st::profilePhoneTop); _botHelp.move(_botSettings.x() + (_botSettings.isHidden() ? 0 : _botSettings.width() + st::profilePhoneLeft), _botSettings.y()); } _pinnedMessage.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop); top += st::profilePhotoSize; top += st::profileButtonTop; _uploadPhoto.setGeometry(_left, top, btnWidth, _uploadPhoto.height()); if (_peerChannel && _peerChannel->count < Global::MegagroupSizeMax() && _peerChannel->isMegagroup() && !_amCreator && !_peerChannel->amEditor() && _peerChannel->canAddParticipants()) { _addParticipant.setGeometry(_left, top, btnWidth, _addParticipant.height()); } else { _addParticipant.setGeometry(_left + _width - btnWidth, top, btnWidth, _addParticipant.height()); } _sendMessage.setGeometry(_left, top, btnWidth, _sendMessage.height()); _shareContact.setGeometry(_left + _width - btnWidth, top, btnWidth, _shareContact.height()); _inviteToGroup.setGeometry(_left + _width - btnWidth, top, btnWidth, _inviteToGroup.height()); if ((!_peerChat || _peerChat->canEdit()) && (!_peerChannel || _amCreator || (_peerChannel->canAddParticipants() && _peerChannel->isMegagroup()))) { top += _shareContact.height(); } else { top -= st::profileButtonTop; } // about if (!_about.isEmpty()) { top += st::profileHeaderSkip; _aboutTop = top; _aboutHeight = _about.countHeight(_width); top += _aboutHeight; } else { _aboutTop = _aboutHeight = 0; } // migrate to megagroup if (_showMigrate) { top += st::profileHeaderSkip; top += _aboutMigrate.countHeight(_width) + st::setLittleSkip; top += st::normalFont->height * 4 + st::setLittleSkip * 3 + st::setSectionSkip; _migrate.move(_left, top); top += _migrate.height(); } // settings top += st::profileHeaderSkip; // invite link stuff int32 _inviteLinkTextWidth(st::linkFont->width(lang(lng_group_invite_link)) + st::linkFont->spacew); if (_amCreator && ((_peerChat && _peerChat->canEdit()) || (_peerChannel && !_peerChannel->isPublic()))) { if (!_invitationText.isEmpty()) { _invitationLink.setText(st::linkFont->elided(_invitationText, _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 top += st::profileHeaderSkip; bool mediaFound = false; for (int i = 0; i < OverviewCount; ++i) { _mediaButtons[i]->move(_left, top); if (!_mediaButtons[i]->isHidden()) { mediaFound = true; top += _mediaButtons[i]->height() + st::setLittleSkip; } } if (_notAllMediaLoaded || !mediaFound) { top += _mediaButtons[OverviewPhotos]->height(); } else { top -= st::setLittleSkip; } // actions top += st::profileHeaderSkip; _searchInPeer.move(_left, top); top += _searchInPeer.height() + st::setLittleSkip; if (_peerChat && _amCreator && !_showMigrate) { _convertToSupergroup.move(_left, top); top += _convertToSupergroup.height() + st::setLittleSkip; } if (_peerUser || _peerChat) { _clearHistory.move(_left, top); top += _clearHistory.height() + st::setLittleSkip; } if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { _deleteConversation.move(_left, top); top += _deleteConversation.height(); } if (_peerUser && peerToUser(_peerUser->id) != MTP::authedId()) { top += st::setSectionSkip; _blockUser.move(_left, top); top += _blockUser.height(); } else if (canDeleteChannel()) { top += (_peerChannel->isMegagroup() ? 0 : (st::setSectionSkip - st::setLittleSkip)); _deleteChannel.move(_left, top); top += _deleteChannel.height(); } // participants if ((_peerChat && _peerChat->amIn()) || (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->amIn())) { top += st::profileHeaderSkip; if (!_participants.isEmpty()) { int32 fullCnt = _participants.size(); top += fullCnt * _pHeight; } } top += st::profileHeaderTop + st::profileHeaderFont->ascent - st::linkFont->ascent; } void ProfileInner::contextMenuEvent(QContextMenuEvent *e) { if (_menu) { _menu->deleteLater(); _menu = 0; } if (!_phoneText.isEmpty() || _peerUser) { QRect info(_left + st::profilePhotoSize + st::profilePhoneLeft, st::profilePadding.top(), _width - st::profilePhotoSize - st::profilePhoneLeft, st::profilePhotoSize); if (info.contains(mapFromGlobal(e->globalPos()))) { _menu = new PopupMenu(); if (_peerUser) { _menu->addAction(lang(lng_profile_copy_fullname), this, SLOT(onCopyFullName()))->setEnabled(true); } if (!_phoneText.isEmpty()) { _menu->addAction(lang(lng_profile_copy_phone), this, SLOT(onCopyPhone()))->setEnabled(true); } if (_peerUser && !_peerUser->username.isEmpty()) { _menu->addAction(lang(lng_context_copy_mention), this, SLOT(onCopyUsername()))->setEnabled(true); } connect(_menu, SIGNAL(destroyed(QObject*)), this, SLOT(onMenuDestroy(QObject*))); _menu->popup(e->globalPos()); e->accept(); } } } void ProfileInner::onMenuDestroy(QObject *obj) { if (_menu == obj) { _menu = 0; } } void ProfileInner::onCopyFullName() { if (!_peerUser) return; QApplication::clipboard()->setText(lng_full_name(lt_first_name, _peerUser->firstName, lt_last_name, _peerUser->lastName).trimmed()); } void ProfileInner::onCopyPhone() { QApplication::clipboard()->setText(_phoneText); } void ProfileInner::onCopyUsername() { if (!_peerUser) return; QApplication::clipboard()->setText('@' + _peerUser->username); } void ProfileInner::step_photo(float64 ms, bool timer) { float64 dt = ms / st::setPhotoDuration; if (dt >= 1) { _a_photo.stop(); a_photoOver.finish(); } else { a_photoOver.update(dt, anim::linear); } if (timer) update(_left, st::profilePadding.top(), st::setPhotoSize, st::setPhotoSize); } PeerData *ProfileInner::peer() const { return _peer; } ProfileInner::~ProfileInner() { for (ParticipantsData::iterator i = _participantsData.begin(), e = _participantsData.end(); i != e; ++i) { delete *i; } _participantsData.clear(); } void ProfileInner::openContextImage() { } void ProfileInner::deleteContextImage() { } void ProfileInner::updateNotifySettings() { _enableNotifications.setChecked(_peer->notify == EmptyNotifySettings || _peer->notify == UnknownNotifySettings || _peer->notify->mute < unixtime()); } int32 ProfileInner::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { int32 result = 0; if (peer == _peer || (_migrated && _migrated->peer == peer)) { if (updateMediaLinks(&result)) { showAll(); resizeEvent(0); update(); } } return result; } void ProfileInner::requestHeight(int32 newHeight) { if (newHeight > height()) { _addToHeight += newHeight - height(); showAll(); } } int32 ProfileInner::countMinHeight() { int32 h = 0; if (_peerUser) { if (peerToUser(_peerUser->id) == MTP::authedId()) { h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip; } else { h = _blockUser.y() + _blockUser.height() + st::profileHeaderSkip; } } else if (_peerChat) { h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip; if (!_participants.isEmpty()) { h += st::profileHeaderSkip + _participants.size() * _pHeight; } else if (_peerChat->amIn()) { h += st::profileHeaderSkip; } } else if (_peerChannel) { if (canDeleteChannel()) { h = _deleteChannel.y() + _deleteChannel.height() + st::profileHeaderSkip; } else if (_peerChannel->amIn() && !_amCreator) { h = _deleteConversation.y() + _deleteConversation.height() + st::profileHeaderSkip; } else { h = _searchInPeer.y() + _searchInPeer.height() + st::profileHeaderSkip; } if (_peerChannel->isMegagroup()) { if (!_participants.isEmpty()) { h += st::profileHeaderSkip + _participants.size() * _pHeight; } else if (_peerChannel->amIn()) { h += st::profileHeaderSkip; } } } return h; } void ProfileInner::allowDecreaseHeight(int32 decreaseBy) { if (decreaseBy > 0 && _addToHeight > 0) { _addToHeight -= qMin(decreaseBy, _addToHeight); showAll(); } } void ProfileInner::showAll() { _searchInPeer.show(); if (_peerChat && _amCreator && !_showMigrate) { _convertToSupergroup.show(); } else { _convertToSupergroup.hide(); } if (_peerUser || _peerChat) { _clearHistory.show(); } else { _clearHistory.hide(); } if (_peerUser || _peerChat || (_peerChannel->amIn() && !_amCreator)) { _deleteConversation.show(); } else { _deleteConversation.hide(); } if (_peerUser) { _uploadPhoto.hide(); _cancelPhoto.hide(); _addParticipant.hide(); _createInvitationLink.hide(); _invitationLink.hide(); _sendMessage.show(); if (_peerUser->phone.isEmpty()) { _shareContact.hide(); if (_peerUser->botInfo && !_peerUser->botInfo->cantJoinGroups) { _inviteToGroup.show(); } else { _inviteToGroup.hide(); } } else { _shareContact.show(); _inviteToGroup.hide(); } _clearHistory.show(); if (peerToUser(_peerUser->id) != MTP::authedId()) { _blockUser.show(); } else { _blockUser.hide(); } _deleteChannel.hide(); _username.hide(); _members.hide(); _admins.hide(); } else if (_peerChat) { _sendMessage.hide(); _shareContact.hide(); _inviteToGroup.hide(); if (!_peerChat->canEdit()) { _uploadPhoto.hide(); _cancelPhoto.hide(); _addParticipant.hide(); _createInvitationLink.hide(); _invitationLink.hide(); } else { if (App::app()->isPhotoUpdating(_peer->id)) { _uploadPhoto.hide(); _cancelPhoto.show(); } else { _uploadPhoto.show(); _cancelPhoto.hide(); } if (_amCreator) { _createInvitationLink.show(); if (_peerChat->invitationUrl.isEmpty()) { _invitationLink.hide(); } else { _invitationLink.show(); } } else { _createInvitationLink.hide(); _invitationLink.hide(); } if (_peerChat->count < Global::ChatSizeMax() && !_showMigrate) { _addParticipant.show(); } else { _addParticipant.hide(); } } _blockUser.hide(); _deleteChannel.hide(); _username.hide(); _members.hide(); if (_amCreator && _peerChat->canEdit()) { _admins.show(); } else { _admins.hide(); } } else if (_peerChannel) { _sendMessage.hide(); _shareContact.hide(); _inviteToGroup.hide(); if (_peerChannel->isForbidden) { _uploadPhoto.hide(); _cancelPhoto.hide(); _createInvitationLink.hide(); _invitationLink.hide(); } else { if (App::app()->isPhotoUpdating(_peer->id)) { _uploadPhoto.hide(); _cancelPhoto.show(); } else { if (_amCreator || (_peerChannel->amEditor() && _peerChannel->isMegagroup())) { _uploadPhoto.show(); } else { _uploadPhoto.hide(); } _cancelPhoto.hide(); } if (_amCreator && !_peerChannel->isPublic()) { _createInvitationLink.show(); if (_peerChannel->invitationUrl.isEmpty()) { _invitationLink.hide(); } else { _invitationLink.show(); } } else { _createInvitationLink.hide(); _invitationLink.hide(); } } if (_peerChannel->count < Global::MegagroupSizeMax() && _peerChannel->isMegagroup() && _peerChannel->canAddParticipants()) { _addParticipant.show(); } else { _addParticipant.hide(); } _blockUser.hide(); if (canDeleteChannel()) { _deleteChannel.show(); } else { _deleteChannel.hide(); } if (_peerChannel->isPublic() || _peerChannel->canEditUsername()) { _username.show(); } else { _username.hide(); } if (_amCreator || _peerChannel->amEditor() || _peerChannel->amModerator()) { _admins.show(); } else { _admins.hide(); } if (_peerChannel->canViewParticipants() && !_peerChannel->isMegagroup()) { _members.show(); } else { _members.hide(); } } if (_showMigrate) { _migrate.show(); } else { _migrate.hide(); } _enableNotifications.show(); updateNotifySettings(); // participants reorderParticipants(); resize(width(), countMinHeight() + _addToHeight); } void ProfileInner::updateInvitationLink() { if (!_peerChat && !_peerChannel) return; if ((_peerChat && _peerChat->invitationUrl.isEmpty()) || (_peerChannel && _peerChannel->invitationUrl.isEmpty())) { _createInvitationLink.setText(lang(lng_group_invite_create)); } else { _createInvitationLink.setText(lang(lng_group_invite_create_new)); _invitationText = _peerChat ? _peerChat->invitationUrl : _peerChannel->invitationUrl; if (_invitationText.startsWith(qstr("http://"), Qt::CaseInsensitive)) { _invitationText = _invitationText.mid(7); } else if (_invitationText.startsWith(qstr("https://"), Qt::CaseInsensitive)) { _invitationText = _invitationText.mid(8); } } } void ProfileInner::updatePinnedMessageVisibility() { if (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->mgInfo->pinnedMsgId && !_amCreator && !_peerChannel->amEditor()) { _pinnedMessage.show(); } else { _pinnedMessage.hide(); } } void ProfileInner::updateBotLinksVisibility() { if (!_peerUser || !_peerUser->botInfo || _peerUser->botInfo->commands.isEmpty()) { _botSettings.hide(); _botHelp.hide(); return; } bool hasSettings = false, hasHelp = false; for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) { QString cmd = _peerUser->botInfo->commands.at(i).command; hasSettings |= !cmd.compare(qsl("settings"), Qt::CaseInsensitive); hasHelp |= !cmd.compare(qsl("help"), Qt::CaseInsensitive); if (hasSettings && hasHelp) break; } _botSettings.setVisible(hasSettings); _botHelp.setVisible(hasHelp); } QString ProfileInner::overviewLinkText(int32 type, int32 count) { switch (type) { case OverviewPhotos: return lng_profile_photos(lt_count, count); case OverviewVideos: return lng_profile_videos(lt_count, count); case OverviewMusicFiles: return lng_profile_songs(lt_count, count); case OverviewFiles: return lng_profile_files(lt_count, count); case OverviewVoiceFiles: return lng_profile_audios(lt_count, count); case OverviewLinks: return lng_profile_shared_links(lt_count, count); } return QString(); } ProfileWidget::ProfileWidget(QWidget *parent, PeerData *peer) : TWidget(parent) , _scroll(this, st::setScroll) , _inner(this, &_scroll, peer) , _a_show(animation(this, &ProfileWidget::step_show)) , _sideShadow(this, st::shadowColor) , _topShadow(this, st::shadowColor) , _inGrab(false) { _scroll.setWidget(&_inner); _scroll.move(0, 0); _inner.move(0, 0); _scroll.show(); _sideShadow.setVisible(!Adaptive::OneColumn()); connect(&_scroll, SIGNAL(scrolled()), &_inner, SLOT(updateSelected())); connect(&_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); } void ProfileWidget::onScroll() { _inner.loadProfilePhotos(_scroll.scrollTop()); if (!_scroll.isHidden() && _scroll.scrollTop() < _scroll.scrollTopMax()) { _inner.allowDecreaseHeight(_scroll.scrollTopMax() - _scroll.scrollTop()); } if (peer()->isMegagroup() && !peer()->asChannel()->mgInfo->lastParticipants.isEmpty() && peer()->asChannel()->mgInfo->lastParticipants.size() < peer()->asChannel()->count) { if (_scroll.scrollTop() + PreloadHeightsCount * _scroll.height() > _scroll.scrollTopMax()) { App::api()->requestLastParticipants(peer()->asChannel(), false); } } } void ProfileWidget::resizeEvent(QResizeEvent *e) { int32 addToY = App::main() ? App::main()->contentScrollAddToY() : 0; int32 newScrollY = _scroll.scrollTop() + addToY; _scroll.resize(size()); _inner.resize(width(), _inner.height()); if (!_scroll.isHidden()) { if (addToY) { _scroll.scrollToY(newScrollY); } if (_scroll.scrollTop() < _scroll.scrollTopMax()) { _inner.allowDecreaseHeight(_scroll.scrollTopMax() - _scroll.scrollTop()); } } _topShadow.resize(width() - ((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0), st::lineWidth); _topShadow.moveToLeft((!Adaptive::OneColumn() && !_inGrab) ? st::lineWidth : 0, 0); _sideShadow.resize(st::lineWidth, height()); _sideShadow.moveToLeft(0, 0); } void ProfileWidget::mousePressEvent(QMouseEvent *e) { } void ProfileWidget::paintEvent(QPaintEvent *e) { if (App::wnd() && App::wnd()->contentOverlapped(this, e)) return; Painter p(this); if (_a_show.animating()) { if (a_coordOver.current() > 0) { p.drawPixmap(QRect(0, 0, a_coordOver.current(), height()), _cacheUnder, QRect(-a_coordUnder.current() * cRetinaFactor(), 0, a_coordOver.current() * cRetinaFactor(), height() * cRetinaFactor())); p.setOpacity(a_shadow.current() * st::slideFadeOut); p.fillRect(0, 0, a_coordOver.current(), height(), st::black->b); p.setOpacity(1); } p.drawPixmap(a_coordOver.current(), 0, _cacheOver); p.setOpacity(a_shadow.current()); p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), height()), App::sprite(), st::slideShadow); } else { p.fillRect(e->rect(), st::white->b); } } void ProfileWidget::dragEnterEvent(QDragEnterEvent *e) { } void ProfileWidget::dropEvent(QDropEvent *e) { } void ProfileWidget::keyPressEvent(QKeyEvent *e) { return _inner.keyPressEvent(e); } void ProfileWidget::paintTopBar(QPainter &p, float64 over, int32 decreaseWidth) { if (_a_show.animating()) { p.drawPixmap(a_coordUnder.current(), 0, _cacheTopBarUnder); p.drawPixmap(a_coordOver.current(), 0, _cacheTopBarOver); p.setOpacity(a_shadow.current()); p.drawPixmap(QRect(a_coordOver.current() - st::slideShadow.pxWidth(), 0, st::slideShadow.pxWidth(), st::topBarHeight), App::sprite(), st::slideShadow); return; } p.setOpacity(st::topBarBackAlpha + (1 - st::topBarBackAlpha) * over); p.drawPixmap(QPoint(st::topBarBackPadding.left(), (st::topBarHeight - st::topBarBackImg.pxHeight()) / 2), App::sprite(), st::topBarBackImg); p.setFont(st::topBarBackFont->f); p.setPen(st::topBarBackColor->p); p.drawText(st::topBarBackPadding.left() + st::topBarBackImg.pxWidth() + st::topBarBackPadding.right(), (st::topBarHeight - st::topBarBackFont->height) / 2 + st::topBarBackFont->ascent, lang(peer()->isUser() ? lng_profile_info : ((peer()->isChat() || peer()->isMegagroup()) ? lng_profile_group_info : lng_profile_channel_info))); } void ProfileWidget::topBarClick() { App::main()->showBackFromStack(); } PeerData *ProfileWidget::peer() const { return _inner.peer(); } int32 ProfileWidget::lastScrollTop() const { return _scroll.scrollTop(); } void ProfileWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTopBarCache, bool back, int32 lastScrollTop) { if (App::app()) App::app()->mtpPause(); if (!cAutoPlayGif()) { App::stopGifItems(); } (back ? _cacheOver : _cacheUnder) = bgAnimCache; (back ? _cacheTopBarOver : _cacheTopBarUnder) = bgAnimTopBarCache; if (lastScrollTop >= 0) _scroll.scrollToY(lastScrollTop); (back ? _cacheUnder : _cacheOver) = myGrab(this); App::main()->topBar()->stopAnim(); (back ? _cacheTopBarUnder : _cacheTopBarOver) = myGrab(App::main()->topBar()); App::main()->topBar()->startAnim(); _scroll.hide(); _topShadow.hide(); a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width())); a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0); a_shadow = back ? anim::fvalue(1, 0) : anim::fvalue(0, 1); _a_show.start(); show(); App::main()->topBar()->update(); _inner.setFocus(); } void ProfileWidget::step_show(float64 ms, bool timer) { float64 dt = ms / st::slideDuration; if (dt >= 1) { _a_show.stop(); _sideShadow.setVisible(!Adaptive::OneColumn()); _topShadow.show(); a_coordUnder.finish(); a_coordOver.finish(); a_shadow.finish(); _cacheUnder = _cacheOver = _cacheTopBarUnder = _cacheTopBarOver = QPixmap(); App::main()->topBar()->stopAnim(); _scroll.show(); _inner.start(); activate(); if (App::app()) App::app()->mtpUnpause(); } else { a_coordUnder.update(dt, st::slideFunction); a_coordOver.update(dt, st::slideFunction); a_shadow.update(dt, st::slideFunction); } if (timer) { update(); App::main()->topBar()->update(); } } void ProfileWidget::updateOnlineDisplay() { _inner.updateOnlineDisplay(); updateOnlineDisplayTimer(); } void ProfileWidget::updateOnlineDisplayTimer() { _inner.updateOnlineDisplayTimer(); } void ProfileWidget::peerUsernameChanged() { _inner.peerUsernameChanged(); } void ProfileWidget::updateNotifySettings() { _inner.updateNotifySettings(); } void ProfileWidget::mediaOverviewUpdated(PeerData *peer, MediaOverviewType type) { int32 addToScroll = _inner.mediaOverviewUpdated(peer, type); if (!_scroll.isHidden() && addToScroll && _scroll.geometry().contains(mapFromGlobal(QCursor::pos()))) { if (addToScroll > 0 && _scroll.scrollTop() + addToScroll > _scroll.scrollTopMax()) { _inner.requestHeight(_scroll.scrollTop() + addToScroll + _scroll.height()); } _scroll.scrollToY(_scroll.scrollTop() + addToScroll); } } void ProfileWidget::updateAdaptiveLayout() { _sideShadow.setVisible(!Adaptive::OneColumn()); } PeerData *ProfileWidget::ui_getPeerForMouseAction() { return _inner.peer(); } void ProfileWidget::clear() { if (_inner.peer() && _inner.peer()->isUser() && _inner.peer()->asUser()->botInfo) { _inner.peer()->asUser()->botInfo->startGroupToken = QString(); } } ProfileWidget::~ProfileWidget() { } void ProfileWidget::activate() { if (_scroll.isHidden()) { setFocus(); } else { _inner.setFocus(); } }