diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0b35ffbc8..876cd811b 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -140,6 +140,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_error_pinned_max#one" = "Sorry, you can pin no more than {count} chat to the top."; "lng_error_pinned_max#other" = "Sorry, you can pin no more than {count} chats to the top."; "lng_error_public_groups_denied" = "Unfortunately, you were banned from participating in public groups.\n{more_info}"; +"lng_error_cant_edit_admin" = "Sorry, you can't edit permissions for this admin."; +"lng_error_cant_add_member" = "Sorry, you can't add the bot to this group."; "lng_edit_deleted" = "This message was deleted"; "lng_edit_too_long" = "Your message text is too long"; @@ -577,6 +579,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_profile_delete_and_exit" = "Leave"; "lng_profile_kick" = "Remove"; "lng_profile_admin" = "admin"; +"lng_profile_edit_admin" = "Edit"; "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?"; @@ -993,7 +996,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_context_unpin_from_top" = "Unpin from top"; "lng_context_promote_admin" = "Promote to admin"; -"lng_context_remove_admin" = "Remove from admins"; +"lng_context_edit_permissions" = "Edit permissions"; +"lng_context_restrict_user" = "Restrict user"; "lng_context_remove_from_group" = "Remove from group"; "lng_context_copy_link" = "Copy Link"; @@ -1246,7 +1250,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_rights_edit_admin" = "Edit administrator"; "lng_rights_edit_admin_header" = "What can this admin do?"; -"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with the same (or more limited) permissions than they have."; +"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with the same (or more limited) permissions."; "lng_rights_about_add_admins_no" = "This admin will not be able to add new admins."; "lng_rights_user_restrictions" = "User restrictions"; "lng_rights_user_restrictions_header" = "What can this user do?"; @@ -1254,12 +1258,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org "lng_rights_channel_info" = "Change channel info"; "lng_rights_channel_post" = "Post messages"; "lng_rights_channel_edit" = "Edit messages of others"; +"lng_rights_channel_delete" = "Delete messages of others"; "lng_rights_group_info" = "Change group info"; "lng_rights_group_ban" = "Ban users"; "lng_rights_group_invite" = "Add users"; -"lng_rights_group_invite_link" = "Invite users via link"; +//"lng_rights_group_invite_link" = "Invite users via link"; "lng_rights_group_pin" = "Pin messages"; -"lng_rights_delete" = "Delete messages of others"; +"lng_rights_group_delete" = "Delete messages"; "lng_rights_add_admins" = "Add new admins"; "lng_rights_chat_read" = "Read messages"; "lng_rights_chat_send_text" = "Send messages"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 2545fab24..7a9280541 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -317,6 +317,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt channel->setAbout(qs(f.vabout)); channel->setMembersCount(f.has_participants_count() ? f.vparticipants_count.v : 0); channel->setAdminsCount(f.has_admins_count() ? f.vadmins_count.v : 0); + channel->setRestrictedCount(f.has_banned_count() ? f.vbanned_count.v : 0); channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0); channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString()); if (auto h = App::historyLoaded(channel->id)) { @@ -335,8 +336,8 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt } channel->fullUpdated(); - if (canViewAdmins != channel->canViewAdmins()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelCanViewAdmins); - if (canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelCanViewMembers); + if (canViewAdmins != channel->canViewAdmins() + || canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelRightsChanged); notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings); } @@ -553,16 +554,26 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP auto needBotsInfos = false; auto botStatus = peer->mgInfo->botStatus; auto keyboardBotFound = !h || !h->lastKeyboardFrom; - auto emptyRights = MTP_channelAdminRights(MTP_flags(0)); + auto emptyAdminRights = MTP_channelAdminRights(MTP_flags(0)); + auto emptyRestrictedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); for_const (auto &participant, v) { auto userId = UserId(0); - auto rights = emptyRights; + auto adminCanEdit = false; + auto adminRights = emptyAdminRights; + auto restrictedRights = emptyRestrictedRights; switch (participant.type()) { case mtpc_channelParticipant: userId = participant.c_channelParticipant().vuser_id.v; break; case mtpc_channelParticipantSelf: userId = participant.c_channelParticipantSelf().vuser_id.v; break; - case mtpc_channelParticipantAdmin: userId = participant.c_channelParticipantAdmin().vuser_id.v; rights = participant.c_channelParticipantAdmin().vadmin_rights; break; - case mtpc_channelParticipantBanned: userId = participant.c_channelParticipantBanned().vuser_id.v; break; + case mtpc_channelParticipantAdmin: + userId = participant.c_channelParticipantAdmin().vuser_id.v; + adminCanEdit = participant.c_channelParticipantAdmin().is_can_edit(); + adminRights = participant.c_channelParticipantAdmin().vadmin_rights; + break; + case mtpc_channelParticipantBanned: + userId = participant.c_channelParticipantBanned().vuser_id.v; + restrictedRights = participant.c_channelParticipantBanned().vbanned_rights; + break; case mtpc_channelParticipantCreator: userId = participant.c_channelParticipantCreator().vuser_id.v; break; } if (!userId) { @@ -570,6 +581,9 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP } auto u = App::user(userId); + if (participant.type() == mtpc_channelParticipantCreator) { + peer->mgInfo->creator = u; + } if (bots) { if (u->botInfo) { peer->mgInfo->bots.insert(u); @@ -584,8 +598,10 @@ void ApiWrap::lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelP } else { if (peer->mgInfo->lastParticipants.indexOf(u) < 0) { peer->mgInfo->lastParticipants.push_back(u); - if (rights.c_channelAdminRights().vflags.v) { - peer->mgInfo->lastAdmins.insert(u, rights); + if (adminRights.c_channelAdminRights().vflags.v) { + peer->mgInfo->lastAdmins.insert(u, MegagroupInfo::Admin { adminRights, adminCanEdit }); + } else if (restrictedRights.c_channelBannedRights().vflags.v != 0) { + peer->mgInfo->lastRestricted.insert(u, MegagroupInfo::Restricted { restrictedRights }); } if (u->botInfo) { peer->mgInfo->bots.insert(u); @@ -647,6 +663,9 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) { auto &d = p.vparticipant.c_channelParticipantCreator(); channel->inviter = _session->userId(); channel->inviteDate = date(MTP_int(channel->date)); + if (channel->mgInfo) { + channel->mgInfo->creator = App::self(); + } } break; case mtpc_channelParticipantAdmin: { auto &d = p.vparticipant.c_channelParticipantAdmin(); @@ -671,44 +690,17 @@ void ApiWrap::kickParticipant(PeerData *peer, UserData *user) { if (_kickRequests.contains(kick)) return; if (auto channel = peer->asChannel()) { - //auto requestId = request(MTPchannels_KickFromChannel(channel->inputChannel, user->inputUser, MTP_bool(true))).done([this, peer, user](const MTPUpdates &result) { - // applyUpdates(result); + auto rights = ChannelData::KickedRestrictedRights(); + auto requestId = request(MTPchannels_EditBanned(channel->inputChannel, user->inputUser, rights)).done([this, channel, user, rights](const MTPUpdates &result) { + applyUpdates(result); - // _kickRequests.remove(KickRequest(peer, user)); - // if (auto channel = peer->asMegagroup()) { - // auto megagroupInfo = channel->mgInfo; + _kickRequests.remove(KickRequest(channel, user)); + channel->applyEditBanned(user, rights); + }).fail([this, kick](const RPCError &error) { + _kickRequests.remove(kick); + }).send(); - // auto i = megagroupInfo->lastParticipants.indexOf(user); - // if (i >= 0) { - // megagroupInfo->lastParticipants.removeAt(i); - // } - - // if (channel->membersCount() > 1) { - // channel->setMembersCount(channel->membersCount() - 1); - // } else { - // megagroupInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated; - // megagroupInfo->lastParticipantsCount = 0; - // } - // channel->setKickedCount(channel->kickedCount() + 1); - // if (megagroupInfo->lastAdmins.contains(user)) { - // megagroupInfo->lastAdmins.remove(user); - // if (channel->adminsCount() > 1) { - // channel->setAdminsCount(channel->adminsCount() - 1); - // } - // Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::AdminsChanged); - // } - // megagroupInfo->bots.remove(user); - // if (megagroupInfo->bots.isEmpty() && megagroupInfo->botStatus > 0) { - // megagroupInfo->botStatus = -1; - // } - // } - // Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); - // fullPeerUpdated().notify(peer); - //}).fail([this, kick](const RPCError &error) { - // _kickRequests.remove(kick); - //}).send(); - - //_kickRequests.insert(kick, requestId); + _kickRequests.insert(kick, requestId); } } @@ -717,22 +709,22 @@ void ApiWrap::unblockParticipant(PeerData *peer, UserData *user) { if (_kickRequests.contains(kick)) return; if (auto channel = peer->asChannel()) { - //auto requestId = request(MTPchannels_KickFromChannel(channel->inputChannel, user->inputUser, MTP_bool(false))).done([this, peer, user](const MTPUpdates &result) { - // applyUpdates(result); + auto requestId = request(MTPchannels_EditBanned(channel->inputChannel, user->inputUser, MTP_channelBannedRights(MTP_flags(0), MTP_int(0)))).done([this, peer, user](const MTPUpdates &result) { + applyUpdates(result); - // _kickRequests.remove(KickRequest(peer, user)); - // if (auto channel = peer->asMegagroup()) { - // if (channel->kickedCount() > 0) { - // channel->setKickedCount(channel->kickedCount() - 1); - // } else { - // channel->updateFull(true); - // } - // } - //}).fail([this, kick](const RPCError &error) { - // _kickRequests.remove(kick); - //}).send(); + _kickRequests.remove(KickRequest(peer, user)); + if (auto channel = peer->asMegagroup()) { + if (channel->kickedCount() > 0) { + channel->setKickedCount(channel->kickedCount() - 1); + } else { + channel->updateFull(true); + } + } + }).fail([this, kick](const RPCError &error) { + _kickRequests.remove(kick); + }).send(); - //_kickRequests.insert(kick, requestId); + _kickRequests.insert(kick, requestId); } } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index d6f7b082c..aba213c8d 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -681,11 +681,9 @@ namespace { auto cdata = data->asChannel(); auto wasInChannel = cdata->amIn(); - auto canEditInformation = cdata->canEditInformation(); auto canViewAdmins = cdata->canViewAdmins(); auto canViewMembers = cdata->canViewMembers(); auto canAddMembers = cdata->canAddMembers(); - auto wasEditor = cdata->amEditor(); if (minimal) { auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy; @@ -694,8 +692,8 @@ namespace { if (d.has_admin_rights() || cdata->hasAdminRights()) { cdata->setAdminRights(d.has_admin_rights() ? d.vadmin_rights : MTP_channelAdminRights(MTP_flags(0))); } - if (d.has_banned_rights() || cdata->hasBannedRights()) { - cdata->setBannedRights(d.has_banned_rights() ? d.vbanned_rights : MTP_channelBannedRights(MTP_flags(0), MTP_int(0))); + if (d.has_banned_rights() || cdata->hasRestrictedRights()) { + cdata->setRestrictedRights(d.has_banned_rights() ? d.vbanned_rights : MTP_channelBannedRights(MTP_flags(0), MTP_int(0))); } cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); cdata->access = d.vaccess_hash.v; @@ -719,14 +717,9 @@ namespace { cdata->setPhoto(d.vphoto); if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn; - if (canEditInformation != cdata->canEditInformation()) update.flags |= UpdateFlag::ChannelCanEditInformation; - if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins; - if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers; - if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers; - if (wasEditor != cdata->amEditor()) { - cdata->selfAdminUpdated(); - update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged); - } + if (canViewAdmins != cdata->canViewAdmins() + || canViewMembers != cdata->canViewMembers() + || canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelRightsChanged; } break; case mtpc_channelForbidden: { auto &d(chat.c_channelForbidden()); @@ -737,11 +730,9 @@ namespace { auto cdata = data->asChannel(); auto wasInChannel = cdata->amIn(); - auto canEditInformation = cdata->canEditInformation(); auto canViewAdmins = cdata->canViewAdmins(); auto canViewMembers = cdata->canViewMembers(); auto canAddMembers = cdata->canAddMembers(); - auto wasEditor = cdata->amEditor(); cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash); @@ -752,8 +743,8 @@ namespace { if (cdata->hasAdminRights()) { cdata->setAdminRights(MTP_channelAdminRights(MTP_flags(0))); } - if (cdata->hasBannedRights()) { - cdata->setBannedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0))); + if (cdata->hasRestrictedRights()) { + cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0))); } cdata->setName(qs(d.vtitle), QString()); @@ -765,14 +756,9 @@ namespace { cdata->setIsForbidden(true); if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn; - if (canEditInformation != cdata->canEditInformation()) update.flags |= UpdateFlag::ChannelCanEditInformation; - if (canViewAdmins != cdata->canViewAdmins()) update.flags |= UpdateFlag::ChannelCanViewAdmins; - if (canViewMembers != cdata->canViewMembers()) update.flags |= UpdateFlag::ChannelCanViewMembers; - if (canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelCanAddMembers; - if (wasEditor != cdata->amEditor()) { - cdata->selfAdminUpdated(); - update.flags |= (UpdateFlag::ChannelAmEditor | UpdateFlag::AdminsChanged); - } + if (canViewAdmins != cdata->canViewAdmins() + || canViewMembers != cdata->canViewMembers() + || canAddMembers != cdata->canAddMembers()) update.flags |= UpdateFlag::ChannelRightsChanged; } break; } if (!data) { @@ -825,10 +811,10 @@ namespace { auto &v = d.vparticipants.v; chat->count = v.size(); int32 pversion = chat->participants.isEmpty() ? 1 : (chat->participants.begin().value() + 1); - chat->invitedByMe = ChatData::InvitedByMe(); - chat->admins = ChatData::Admins(); + chat->invitedByMe.clear(); + chat->admins.clear(); chat->flags &= ~MTPDchat::Flag::f_admin; - for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { + for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) { int32 uid = 0, inviter = 0; switch (i->type()) { case mtpc_chatParticipantCreator: { @@ -870,7 +856,7 @@ namespace { History *h = App::historyLoaded(chat->id); bool found = !h || !h->lastKeyboardFrom; int32 botStatus = -1; - for (ChatData::Participants::iterator i = chat->participants.begin(), e = chat->participants.end(); i != e;) { + for (auto i = chat->participants.begin(), e = chat->participants.end(); i != e;) { if (i.value() < pversion) { i = chat->participants.erase(i); } else { @@ -976,7 +962,7 @@ namespace { chat->count--; } } else { - ChatData::Participants::iterator i = chat->participants.find(user); + auto i = chat->participants.find(user); if (i != chat->participants.end()) { chat->participants.erase(i); chat->count--; @@ -993,7 +979,7 @@ namespace { } if (chat->botStatus > 0 && user->botInfo) { int32 botStatus = -1; - for (ChatData::Participants::const_iterator j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) { + for (auto j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) { if (j.key()->botInfo) { if (true || botStatus > 0/* || !j.key()->botInfo->readsAllHistory*/) { botStatus = 2; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 622560bf5..469977f75 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -938,7 +938,7 @@ void EditChannelBox::prepare() { connect(_publicLink, SIGNAL(clicked()), this, SLOT(onPublicLink())); _publicLink->setVisible(_channel->canEditUsername()); - _sign->setVisible(!_channel->isMegagroup()); + _sign->setVisible(canEditSignatures()); updateMaxHeight(); } @@ -969,10 +969,14 @@ void EditChannelBox::onDescriptionResized() { update(); } +bool EditChannelBox::canEditSignatures() const { + return _channel->amCreator() && !_channel->isMegagroup(); +} + void EditChannelBox::updateMaxHeight() { auto newHeight = st::newGroupInfoPadding.top() + _title->height(); newHeight += st::newGroupDescriptionPadding.top() + _description->height() + st::newGroupDescriptionPadding.bottom(); - if (!_channel->isMegagroup()) { + if (canEditSignatures()) { newHeight += st::newGroupPublicLinkPadding.top() + _sign->heightNoMargins() + st::newGroupPublicLinkPadding.bottom(); } if (_channel->canEditUsername()) { @@ -993,10 +997,10 @@ void EditChannelBox::resizeEvent(QResizeEvent *e) { _sign->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); - if (_channel->isMegagroup()) { - _publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); - } else { + if (canEditSignatures()) { _publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _sign->bottomNoMargins() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); + } else { + _publicLink->moveToLeft(st::boxPadding.left() + st::newGroupInfoPadding.left(), _description->y() + _description->height() + st::newGroupDescriptionPadding.bottom() + st::newGroupPublicLinkPadding.top()); } } @@ -1031,7 +1035,7 @@ void EditChannelBox::saveDescription() { } void EditChannelBox::saveSign() { - if (_channel->isMegagroup() || _channel->addsSignature() == _sign->checked()) { + if (!canEditSignatures() || _channel->addsSignature() == _sign->checked()) { closeBox(); } else { _saveSignRequestId = MTP::send(MTPchannels_ToggleSignatures(_channel->inputChannel, MTP_bool(_sign->checked())), rpcDone(&EditChannelBox::onSaveSignDone), rpcFail(&EditChannelBox::onSaveFail)); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index c742c7542..9014d5d7c 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -255,6 +255,7 @@ private slots: private: void updateMaxHeight(); + bool canEditSignatures() const; void onSaveTitleDone(const MTPUpdates &updates); void onSaveDescriptionDone(const MTPBool &result); diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 8cef44744..47ab08844 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -532,7 +532,7 @@ void DeleteMessagesBox::deleteAndClear() { if (_moderateFrom) { if (_banUser && _banUser->checked()) { -// MTP::send(MTPchannels_KickFromChannel(_moderateInChannel->inputChannel, _moderateFrom->inputUser, MTP_boolTrue()), App::main()->rpcDone(&MainWidget::sentUpdatesReceived)); + App::api()->kickParticipant(_moderateInChannel, _moderateFrom); } if (_reportSpam->checked()) { MTP::send(MTPchannels_ReportSpam(_moderateInChannel->inputChannel, _moderateFrom->inputUser, MTP_vector(1, MTP_int(_ids[0].msg)))); diff --git a/Telegram/SourceFiles/boxes/contacts_box.cpp b/Telegram/SourceFiles/boxes/contacts_box.cpp index 5393ea95c..1d52e6c78 100644 --- a/Telegram/SourceFiles/boxes/contacts_box.cpp +++ b/Telegram/SourceFiles/boxes/contacts_box.cpp @@ -344,8 +344,8 @@ void ContactsBox::onPeerSelectedChanged(PeerData *peer, bool checked) { } void ContactsBox::inviteParticipants() { - QVector users(_inner->selected()); - if (users.isEmpty()) { + auto users = _inner->selected(); + if (users.empty()) { _select->entity()->setInnerFocus(); return; } @@ -363,7 +363,7 @@ void ContactsBox::createGroup() { if (_saveRequestId) return; auto users = _inner->selectedInputs(); - if (users.isEmpty() || (users.size() == 1 && users.at(0).type() == mtpc_inputUserSelf)) { + if (users.empty() || (users.size() == 1 && users.at(0).type() == mtpc_inputUserSelf)) { _select->entity()->setInnerFocus(); return; } @@ -396,15 +396,16 @@ void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) { closeBox(); return; } - ChatData::Admins curadmins = _inner->chat()->admins; - QVector newadmins = _inner->selected(), appoint; - if (!newadmins.isEmpty()) { + auto curadmins = _inner->chat()->admins; + auto newadmins = _inner->selected(); + auto appoint = decltype(newadmins)(); + if (!newadmins.empty()) { appoint.reserve(newadmins.size()); - for (int32 i = 0, l = newadmins.size(); i < l; ++i) { - ChatData::Admins::iterator c = curadmins.find(newadmins.at(i)); + for (auto &user : newadmins) { + auto c = curadmins.find(user); if (c == curadmins.cend()) { - if (newadmins.at(i)->id != peerFromUser(_inner->chat()->creator)) { - appoint.push_back(newadmins.at(i)); + if (user->id != peerFromUser(_inner->chat()->creator)) { + appoint.push_back(user); } } else { curadmins.erase(c); @@ -413,10 +414,10 @@ void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) { } _saveRequestId = 0; - for_const (UserData *user, curadmins) { + for_const (auto user, curadmins) { MTP::send(MTPmessages_EditChatAdmin(_inner->chat()->inputChat, user->inputUser, MTP_boolFalse()), rpcDone(&ContactsBox::removeAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10); } - for_const (UserData *user, appoint) { + for_const (auto user, appoint) { MTP::send(MTPmessages_EditChatAdmin(_inner->chat()->inputChat, user->inputUser, MTP_boolTrue()), rpcDone(&ContactsBox::setAdminDone, user), rpcFail(&ContactsBox::editAdminFail), 0, 10); } MTP::sendAnything(); @@ -427,7 +428,7 @@ void ContactsBox::getAdminsDone(const MTPmessages_ChatFull &result) { } } -void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) { +void ContactsBox::setAdminDone(gsl::not_null user, const MTPBool &result) { if (mtpIsTrue(result)) { if (_inner->chat()->noParticipantInfo()) { App::api()->requestFullPeer(_inner->chat()); @@ -442,7 +443,7 @@ void ContactsBox::setAdminDone(UserData *user, const MTPBool &result) { } } -void ContactsBox::removeAdminDone(UserData *user, const MTPBool &result) { +void ContactsBox::removeAdminDone(gsl::not_null user, const MTPBool &result) { if (mtpIsTrue(result)) { _inner->chat()->admins.remove(user); } @@ -610,7 +611,7 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent) addDialogsToList([](PeerData *peer) { if (peer->isChat() && peer->asChat()->canEdit()) { return true; - } else if (peer->isMegagroup() && peer->asChannel()->canAddMembers()) { + } else if (peer->isMegagroup()) { return true; } return false; @@ -719,10 +720,10 @@ void ContactsBox::Inner::addBot() { } else if (!info->startGroupToken.isEmpty()) { MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(rand_value()), MTP_string(info->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, { _bot, _addToPeer })); } else { - App::main()->addParticipants(_addToPeer, QVector(1, _bot)); + App::main()->addParticipants(_addToPeer, std::vector>(1, _bot)); } } else { - App::main()->addParticipants(_addToPeer, QVector(1, _bot)); + App::main()->addParticipants(_addToPeer, std::vector>(1, _bot)); } Ui::hideLayer(); Ui::showPeerHistory(_addToPeer, ShowAtUnreadMsgId); @@ -742,21 +743,8 @@ void ContactsBox::Inner::addAdminDone(MTPChannelAdminRights rights, const MTPUpd if (req != _addAdminRequestId) return; _addAdminRequestId = 0; - if (_addAdmin && _channel && _channel->isMegagroup()) { - Notify::PeerUpdate update(_channel); - if (_channel->mgInfo->lastParticipants.indexOf(_addAdmin) < 0) { - _channel->mgInfo->lastParticipants.push_front(_addAdmin); - update.flags |= Notify::PeerUpdate::Flag::MembersChanged; - } - _channel->mgInfo->lastAdmins.insert(_addAdmin, rights); - update.flags |= Notify::PeerUpdate::Flag::AdminsChanged; - if (_addAdmin->botInfo) { - _channel->mgInfo->bots.insert(_addAdmin); - if (_channel->mgInfo->botStatus != 0 && _channel->mgInfo->botStatus < 2) { - _channel->mgInfo->botStatus = 2; - } - } - Notify::peerUpdatedDelayed(update); + if (_addAdmin && _channel) { + _channel->applyEditAdmin(_addAdmin, rights); } if (_addAdminBox) _addAdminBox->closeBox(); emit adminAdded(); @@ -1336,117 +1324,197 @@ void ContactsBox::Inner::setSearchedPressed(int pressed) { _searchedPressed = pressed; } -void ContactsBox::Inner::chooseParticipant() { - if (_saving) return; - bool addingAdmin = (_channel && _membersFilter == MembersFilter::Admins); - if (!addingAdmin && usingMultiSelect()) { - _time = unixtime(); - if (_filter.isEmpty()) { - if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) { - auto data = d_byUsername[_searchedSelected]; - auto peer = _byUsername[_searchedSelected]; - if (data->disabledChecked) return; +void ContactsBox::Inner::changeMultiSelectCheckState() { + _time = unixtime(); + if (_filter.isEmpty()) { + if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) { + auto data = d_byUsername[_searchedSelected]; + auto peer = _byUsername[_searchedSelected]; + if (data->disabledChecked) return; - changeCheckState(data, peer); - } else if (_selected) { - auto data = contactData(_selected); - auto peer = _selected->history()->peer; - if (data->disabledChecked) return; + changeCheckState(data, peer); + } else if (_selected) { + auto data = contactData(_selected); + auto peer = _selected->history()->peer; + if (data->disabledChecked) return; - changeCheckState(_selected); - } - } else { - if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) { - auto data = d_byUsernameFiltered[_searchedSelected]; - auto peer = _byUsernameFiltered[_searchedSelected]; - if (data->disabledChecked) return; - - int i = 0, l = d_byUsername.size(); - for (; i < l; ++i) { - if (d_byUsername[i] == data) { - break; - } - } - if (i == l) { - d_byUsername.push_back(data); - _byUsername.push_back(peer); - for (i = 0, l = _byUsernameDatas.size(); i < l;) { - if (_byUsernameDatas[i] == data) { - _byUsernameDatas.removeAt(i); - --l; - } else { - ++i; - } - } - } - - changeCheckState(data, peer); - } else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) { - auto data = contactData(_filtered[_filteredSelected]); - auto peer = _filtered[_filteredSelected]->history()->peer; - if (data->disabledChecked) return; - - changeCheckState(data, peer); - } + changeCheckState(_selected); } } else { - PeerData *peer = 0; - if (_filter.isEmpty()) { - if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) { - peer = _byUsername[_searchedSelected]; - } else if (_selected) { - peer = _selected->history()->peer; - } - } else { - if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) { - peer = _byUsernameFiltered[_searchedSelected]; - } else { - if (_filteredSelected < 0 || _filteredSelected >= _filtered.size()) return; - peer = _filtered[_filteredSelected]->history()->peer; - } - } - if (peer) { - if (addingAdmin) { - _addAdmin = peer->asUser(); - if (_addAdminRequestId) { - MTP::cancel(_addAdminRequestId); - _addAdminRequestId = 0; + if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) { + auto data = d_byUsernameFiltered[_searchedSelected]; + auto peer = _byUsernameFiltered[_searchedSelected]; + if (data->disabledChecked) return; + + int i = 0, l = d_byUsername.size(); + for (; i < l; ++i) { + if (d_byUsername[i] == data) { + break; } - if (_addAdminBox) _addAdminBox->deleteLater(); - using Right = MTPDchannelAdminRights::Flag; - auto defaultRights = _channel->isMegagroup() - ? (Right::f_change_info | Right::f_delete_messages | Right::f_ban_users | Right::f_invite_users | Right::f_invite_link | Right::f_pin_messages) - : (Right::f_change_info | Right::f_post_messages | Right::f_edit_messages | Right::f_delete_messages); - auto currentRights = (_channel->isMegagroup() ? _channel->mgInfo->lastAdmins : QMap()).value(_addAdmin, MTP_channelAdminRights(MTP_flags(defaultRights))); - _addAdminBox = Ui::show(Box(_channel, _addAdmin, currentRights, base::lambda_guarded(this, [this](const MTPChannelAdminRights &rights) { - if (_addAdminRequestId) return; - _addAdminRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _addAdmin->inputUser, rights), rpcDone(&Inner::addAdminDone, rights), rpcFail(&Inner::addAdminFail)); - })), KeepOtherLayers); - } else if (sharingBotGame()) { - _addToPeer = peer; - auto confirmText = [peer] { - if (peer->isUser()) { - return lng_bot_sure_share_game(lt_user, App::peerName(peer)); - } - return lng_bot_sure_share_game_group(lt_group, peer->name); - }; - Ui::show(Box(confirmText(), base::lambda_guarded(this, [this] { - addBot(); - })), KeepOtherLayers); - } else if (bot() && (peer->isChat() || peer->isMegagroup())) { - _addToPeer = peer; - Ui::show(Box(lng_bot_sure_invite(lt_group, peer->name), base::lambda_guarded(this, [this] { - addBot(); - })), KeepOtherLayers); - } else { - Ui::hideSettingsAndLayer(true); - App::main()->choosePeer(peer->id, ShowAtUnreadMsgId); } + if (i == l) { + d_byUsername.push_back(data); + _byUsername.push_back(peer); + for (i = 0, l = _byUsernameDatas.size(); i < l;) { + if (_byUsernameDatas[i] == data) { + _byUsernameDatas.removeAt(i); + --l; + } else { + ++i; + } + } + } + + changeCheckState(data, peer); + } else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) { + auto data = contactData(_filtered[_filteredSelected]); + auto peer = _filtered[_filteredSelected]->history()->peer; + if (data->disabledChecked) return; + + changeCheckState(data, peer); + } + } +} + +PeerData *ContactsBox::Inner::selectedPeer() const { + if (_filter.isEmpty()) { + if (_searchedSelected >= 0 && _searchedSelected < _byUsername.size()) { + return _byUsername[_searchedSelected]; + } else if (_selected) { + return _selected->history()->peer; + } + } else { + if (_searchedSelected >= 0 && _searchedSelected < _byUsernameFiltered.size()) { + return _byUsernameFiltered[_searchedSelected]; + } else if (_filteredSelected >= 0 && _filteredSelected < _filtered.size()) { + return _filtered[_filteredSelected]->history()->peer; + } + } + return nullptr; +} + +void ContactsBox::Inner::chooseParticipant() { + if (_saving) { + return; + } + + if (usingMultiSelect()) { + changeMultiSelectCheckState(); + } else { + if (_channel && _membersFilter == MembersFilter::Admins) { + addSelectedAsChannelAdmin(); + } else if (sharingBotGame()) { + shareBotGameToSelected(); + } else if (bot()) { + addBotToSelectedGroup(); + } else if (auto peer = selectedPeer()) { + Ui::hideSettingsAndLayer(true); + App::main()->choosePeer(peer->id, ShowAtUnreadMsgId); } } update(); } +void ContactsBox::Inner::addSelectedAsChannelAdmin() { + auto peer = selectedPeer(); + if (!peer) { + return; + } + + _addAdmin = peer->asUser(); + t_assert(_addAdmin != nullptr); + + if (_addAdminRequestId) { + MTP::cancel(_addAdminRequestId); + _addAdminRequestId = 0; + } + if (_addAdminBox) _addAdminBox->deleteLater(); + + auto showBox = [this](auto &¤tRights) { + _addAdminBox = Ui::show(Box(_channel, _addAdmin, currentRights, base::lambda_guarded(this, [this](const MTPChannelAdminRights &rights) { + if (_addAdminRequestId) return; + _addAdminRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _addAdmin->inputUser, rights), rpcDone(&Inner::addAdminDone, rights), rpcFail(&Inner::addAdminFail)); + })), KeepOtherLayers); + }; + + auto loadedRights = [this]() -> const MegagroupInfo::Admin * { + if (_channel->isMegagroup()) { + auto it = _channel->mgInfo->lastAdmins.constFind(_addAdmin); + if (it != _channel->mgInfo->lastAdmins.cend()) { + return &it.value(); + } + } + return nullptr; + }; + + if (auto rights = loadedRights()) { + if (rights->canEdit) { + showBox(rights->rights); + } else { + Ui::show(Box(lang(lng_error_cant_edit_admin)), KeepOtherLayers); + } + } else { + // We don't have current rights yet. + _addAdminRequestId = MTP::send(MTPchannels_GetParticipant(_channel->inputChannel, _addAdmin->inputUser), ::rpcDone(base::lambda_guarded(this, [this, showBox](const MTPchannels_ChannelParticipant &result) { + Expects(result.type() == mtpc_channels_channelParticipant); + auto &participant = result.c_channels_channelParticipant(); + App::feedUsers(participant.vusers); + _addAdminRequestId = 0; + if (participant.vparticipant.type() == mtpc_channelParticipantAdmin) { + if (participant.vparticipant.c_channelParticipantAdmin().is_can_edit()) { + showBox(participant.vparticipant.c_channelParticipantAdmin().vadmin_rights); + } else { + Ui::show(Box(lang(lng_error_cant_edit_admin)), KeepOtherLayers); + } + } else { + showBox(EditAdminBox::DefaultRights(_channel)); + } + })), ::rpcFail(base::lambda_guarded(this, [this](const RPCError &error) { + if (MTP::isDefaultHandledError(error)) { + return false; + } + _addAdminRequestId = 0; + return true; + }))); + } +} + +void ContactsBox::Inner::shareBotGameToSelected() { + _addToPeer = selectedPeer(); + if (!_addToPeer) { + return; + } + + auto confirmText = [this] { + if (_addToPeer->isUser()) { + return lng_bot_sure_share_game(lt_user, App::peerName(_addToPeer)); + } + return lng_bot_sure_share_game_group(lt_group, _addToPeer->name); + }; + Ui::show(Box(confirmText(), base::lambda_guarded(this, [this] { + addBot(); + })), KeepOtherLayers); +} + +void ContactsBox::Inner::addBotToSelectedGroup() { + _addToPeer = selectedPeer(); + if (!_addToPeer) { + return; + } + + if (auto megagroup = _addToPeer->asMegagroup()) { + if (!megagroup->canAddMembers()) { + Ui::show(Box(lang(lng_error_cant_add_member)), KeepOtherLayers); + return; + } + } + if (_addToPeer->isChat() || _addToPeer->isMegagroup()) { + Ui::show(Box(lng_bot_sure_invite(lt_group, _addToPeer->name), base::lambda_guarded(this, [this] { + addBot(); + })), KeepOtherLayers); + } +} + void ContactsBox::Inner::changeCheckState(Dialogs::Row *row) { changeCheckState(contactData(row), row->history()->peer); } @@ -1974,8 +2042,8 @@ void ContactsBox::Inner::selectSkipPage(int32 h, int32 dir) { selectSkip(points * dir); } -QVector ContactsBox::Inner::selected() { - QVector result; +std::vector> ContactsBox::Inner::selected() { + std::vector> result; if (!usingMultiSelect()) { return result; } @@ -1987,13 +2055,17 @@ QVector ContactsBox::Inner::selected() { } result.reserve(_contactsData.size()); for (auto i = _contactsData.cbegin(), e = _contactsData.cend(); i != e; ++i) { - if (i.value()->checkbox->checked() && i.key()->isUser()) { - result.push_back(i.key()->asUser()); + if (i.value()->checkbox->checked()) { + if (auto user = i.key()->asUser()) { + result.push_back(user); + } } } for (int i = 0, l = _byUsername.size(); i < l; ++i) { - if (d_byUsername[i]->checkbox->checked() && _byUsername[i]->isUser()) { - result.push_back(_byUsername[i]->asUser()); + if (d_byUsername[i]->checkbox->checked()) { + if (auto user = _byUsername[i]->asUser()) { + result.push_back(user); + } } } return result; diff --git a/Telegram/SourceFiles/boxes/contacts_box.h b/Telegram/SourceFiles/boxes/contacts_box.h index 572f9eb4a..84bc7992f 100644 --- a/Telegram/SourceFiles/boxes/contacts_box.h +++ b/Telegram/SourceFiles/boxes/contacts_box.h @@ -105,8 +105,8 @@ private: void saveAdminsDone(const MTPUpdates &result); void saveSelectedAdmins(); void getAdminsDone(const MTPmessages_ChatFull &result); - void setAdminDone(UserData *user, const MTPBool &result); - void removeAdminDone(UserData *user, const MTPBool &result); + void setAdminDone(gsl::not_null user, const MTPBool &result); + void removeAdminDone(gsl::not_null user, const MTPBool &result); bool saveAdminsFail(const RPCError &error); bool editAdminFail(const RPCError &error); @@ -163,7 +163,7 @@ public: void selectSkip(int32 dir); void selectSkipPage(int32 h, int32 dir); - QVector selected(); + std::vector> selected(); QVector selectedInputs(); bool allAdmins() const; void setAllAdminsChangedCallback(base::lambda allAdminsChangedCallback) { @@ -268,9 +268,14 @@ private: template void addDialogsToList(FilterCallback callback); + PeerData *selectedPeer() const; bool usingMultiSelect() const { return (_chat != nullptr) || (_creating != CreatingGroupNone && (!_channel || _membersFilter != MembersFilter::Admins)); } + void changeMultiSelectCheckState(); + void addSelectedAsChannelAdmin(); + void shareBotGameToSelected(); + void addBotToSelectedGroup(); base::lambda _peerSelectedChangedCallback; diff --git a/Telegram/SourceFiles/boxes/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/edit_participant_box.cpp index bceef1779..8975f9d2c 100644 --- a/Telegram/SourceFiles/boxes/edit_participant_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_participant_box.cpp @@ -23,16 +23,81 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "lang/lang_keys.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" +#include "ui/widgets/buttons.h" #include "styles/style_boxes.h" +#include "boxes/calendar_box.h" -EditParticipantBox::EditParticipantBox(QWidget*, gsl::not_null channel, gsl::not_null user) : BoxContent() -, _channel(channel) -, _user(user) { +namespace { + +constexpr auto kMaxRestrictDelayDays = 366; + +template +void ApplyDependencies(CheckboxesMap &checkboxes, DependenciesMap &dependencies, QPointer changed) { + auto checkAndApply = [&checkboxes](auto &¤t, auto dependency, bool isChecked) { + for (auto &&checkbox : checkboxes) { + if ((checkbox.first & dependency) && (checkbox.second->checked() == isChecked)) { + current->setChecked(isChecked); + return true; + } + } + return false; + }; + auto applySomeDependency = [&checkboxes, &dependencies, &changed, checkAndApply] { + auto result = false; + for (auto &&entry : checkboxes) { + if (entry.second == changed) { + continue; + } + auto isChecked = entry.second->checked(); + for (auto &&dependency : dependencies) { + if (entry.first & (isChecked ? dependency.first : dependency.second)) { + if (checkAndApply(entry.second, (isChecked ? dependency.second : dependency.first), !isChecked)) { + result = true; + break; + } + } + } + } + return result; + }; + + while (true) { + if (!applySomeDependency()) { + break; + } + }; } -void EditParticipantBox::resizeToContent() { - auto newWidth = st::boxWideWidth; - auto newHeight = 0; +} // namespace + +class EditParticipantBox::Inner : public TWidget { +public: + Inner(QWidget *parent, gsl::not_null channel, gsl::not_null user) : TWidget(parent) + , _channel(channel) + , _user(user) { + } + + template + QPointer addControl(object_ptr row) { + row->setParent(this); + _rows.push_back(std::move(row)); + return static_cast(_rows.back().data()); + } + +protected: + int resizeGetHeight(int newWidth) override; + void resizeEvent(QResizeEvent *e) override; + void paintEvent(QPaintEvent *e) override; + +private: + gsl::not_null _channel; + gsl::not_null _user; + std::vector> _rows; + +}; + +int EditParticipantBox::Inner::resizeGetHeight(int newWidth) { + auto newHeight = st::contactsPhotoSize + st::contactsPadding.bottom(); auto rowWidth = newWidth - st::boxPadding.left() - st::boxPadding.right(); for (auto &&row : _rows) { row->resizeToNaturalWidth(rowWidth); @@ -41,50 +106,111 @@ void EditParticipantBox::resizeToContent() { if (!_rows.empty()) { newHeight += (_rows.size() - 1) * st::boxLittleSkip; } - setDimensions(st::boxWideWidth, newHeight); + return newHeight; } -void EditParticipantBox::resizeEvent(QResizeEvent *e) { - auto top = 0; +void EditParticipantBox::Inner::resizeEvent(QResizeEvent *e) { + auto top = st::contactsPhotoSize + st::contactsPadding.bottom(); for (auto &&row : _rows) { row->moveToLeft(st::boxPadding.left(), top); top += row->heightNoMargins() + st::boxLittleSkip; } } -void EditParticipantBox::paintEvent(QPaintEvent *e) { +void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) { Painter p(this); p.fillRect(e->rect(), st::boxBg); + + _user->paintUserpicLeft(p, st::boxPadding.left(), 0, width(), st::contactsPhotoSize); + + p.setPen(st::contactsNameFg); + auto namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + auto namew = width() - namex - st::contactsPadding.right(); + _user->nameText.drawLeftElided(p, namex, st::contactsNameTop, namew, width()); + auto statusText = [this] { + if (_user->botInfo) { + auto isAdmin = _channel->mgInfo ? _channel->mgInfo->lastAdmins.contains(_user) : false; + auto seesAllMessages = (_user->botInfo->readsAllHistory || isAdmin); + return lang(seesAllMessages ? lng_status_bot_reads_all : lng_status_bot_not_reads_all); + } + return App::onlineText(_user->onlineTill, unixtime()); + }; + p.setFont(st::contactsStatusFont); + p.setPen(st::contactsStatusFg); + p.drawTextLeft(namex, st::contactsStatusTop, width(), statusText()); +} + +EditParticipantBox::EditParticipantBox(QWidget*, gsl::not_null channel, gsl::not_null user) : BoxContent() +, _channel(channel) +, _user(user) { +} + +void EditParticipantBox::prepare() { + _inner = setInnerWidget(object_ptr(this, _channel, _user)); +} + +template +QPointer EditParticipantBox::addControl(object_ptr row) { + Expects(_inner != nullptr); + return _inner->addControl(std::move(row)); +} + +void EditParticipantBox::resizeToContent() { + _inner->resizeToWidth(st::boxWideWidth); + setDimensions(_inner->width(), _inner->height()); } EditAdminBox::EditAdminBox(QWidget*, gsl::not_null channel, gsl::not_null user, const MTPChannelAdminRights &rights, base::lambda callback) : EditParticipantBox(nullptr, channel, user) -, _rights(rights) { +, _rights(rights) +, _saveCallback(std::move(callback)) { + auto dependency = [this](Flag dependent, Flag dependency) { + _dependencies.push_back(std::make_pair(dependent, dependency)); + }; + dependency(Flag::f_invite_link, Flag::f_invite_users); // invite_link <-> invite_users + dependency(Flag::f_invite_users, Flag::f_invite_link); +} + +MTPChannelAdminRights EditAdminBox::DefaultRights(gsl::not_null channel) { + auto defaultRights = channel->isMegagroup() + ? (Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users | Flag::f_invite_users | Flag::f_invite_link | Flag::f_pin_messages) + : (Flag::f_change_info | Flag::f_post_messages | Flag::f_edit_messages | Flag::f_delete_messages); + return MTP_channelAdminRights(MTP_flags(defaultRights)); } void EditAdminBox::prepare() { + EditParticipantBox::prepare(); + setTitle(langFactory(lng_rights_edit_admin)); addControl(object_ptr(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::boxLabel)); auto addCheckbox = [this](Flag flag, const QString &text) { + if (!channel()->amCreator()) { + if (!(channel()->adminRights().vflags.v & flag)) { + return; // Don't add options that we don't have ourselves. + } + } auto checked = (_rights.c_channelAdminRights().vflags.v & flag) != 0; auto control = addControl(object_ptr(this, text, checked, st::defaultBoxCheckbox)); + connect(control, &Ui::Checkbox::changed, this, [this, control] { + applyDependencies(control); + }, Qt::QueuedConnection); _checkboxes.emplace(flag, control); }; if (channel()->isMegagroup()) { addCheckbox(Flag::f_change_info, lang(lng_rights_group_info)); - addCheckbox(Flag::f_delete_messages, lang(lng_rights_delete)); + addCheckbox(Flag::f_delete_messages, lang(lng_rights_group_delete)); addCheckbox(Flag::f_ban_users, lang(lng_rights_group_ban)); addCheckbox(Flag::f_invite_users, lang(lng_rights_group_invite)); - addCheckbox(Flag::f_invite_link, lang(lng_rights_group_invite_link)); +// addCheckbox(Flag::f_invite_link, lang(lng_rights_group_invite_link)); addCheckbox(Flag::f_pin_messages, lang(lng_rights_group_pin)); addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins)); } else { addCheckbox(Flag::f_change_info, lang(lng_rights_channel_info)); addCheckbox(Flag::f_post_messages, lang(lng_rights_channel_post)); addCheckbox(Flag::f_edit_messages, lang(lng_rights_channel_edit)); - addCheckbox(Flag::f_delete_messages, lang(lng_rights_delete)); + addCheckbox(Flag::f_delete_messages, lang(lng_rights_channel_delete)); addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins)); } @@ -96,9 +222,34 @@ void EditAdminBox::prepare() { }); refreshAboutAddAdminsText(); + addButton(langFactory(lng_settings_save), [this] { + if (!_saveCallback) { + return; + } + auto newFlags = MTPDchannelAdminRights::Flags(0); + for (auto &&checkbox : _checkboxes) { + if (checkbox.second->checked()) { + newFlags |= checkbox.first; + } else { + newFlags &= ~checkbox.first; + } + } + _saveCallback(MTP_channelAdminRights(MTP_flags(newFlags))); + }); + addButton(langFactory(lng_cancel), [this] { closeBox(); }); + + applyDependencies(nullptr); + for (auto &&checkbox : _checkboxes) { + checkbox.second->finishAnimations(); + } + resizeToContent(); } +void EditAdminBox::applyDependencies(QPointer changed) { + ApplyDependencies(_checkboxes, _dependencies, changed); +} + void EditAdminBox::refreshAboutAddAdminsText() { auto addAdmins = _checkboxes.find(Flag::f_add_admins); t_assert(addAdmins != _checkboxes.end()); @@ -107,23 +258,105 @@ void EditAdminBox::refreshAboutAddAdminsText() { resizeToContent(); } -EditRestrictedBox::EditRestrictedBox(QWidget*, gsl::not_null channel, gsl::not_null user, const MTPChannelBannedRights &rights, base::lambda callback) : EditParticipantBox(nullptr, channel, user) { +EditRestrictedBox::EditRestrictedBox(QWidget*, gsl::not_null channel, gsl::not_null user, const MTPChannelBannedRights &rights, base::lambda callback) : EditParticipantBox(nullptr, channel, user) +, _rights(rights) +, _until(rights.c_channelBannedRights().vuntil_date.v) +, _saveCallback(std::move(callback)) { + auto dependency = [this](Flag dependent, Flag dependency) { + _dependencies.push_back(std::make_pair(dependent, dependency)); + }; + dependency(Flag::f_send_gifs, Flag::f_send_stickers); // stickers <-> gifs + dependency(Flag::f_send_stickers, Flag::f_send_gifs); + dependency(Flag::f_send_games, Flag::f_send_stickers); // stickers <-> games + dependency(Flag::f_send_stickers, Flag::f_send_games); + dependency(Flag::f_send_inline, Flag::f_send_stickers); // stickers <-> inline + dependency(Flag::f_send_stickers, Flag::f_send_inline); + dependency(Flag::f_send_stickers, Flag::f_send_media); // stickers -> send_media + dependency(Flag::f_embed_links, Flag::f_send_media); // embed_links -> send_media + dependency(Flag::f_send_media, Flag::f_send_messages); // send_media- > send_messages + dependency(Flag::f_send_messages, Flag::f_view_messages); // send_messages -> view_messages } void EditRestrictedBox::prepare() { + EditParticipantBox::prepare(); + setTitle(langFactory(lng_rights_user_restrictions)); - addControl(object_ptr(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::boxLabel)); + addControl(object_ptr(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::boxLabel)); - auto addCheckbox = [this](Flag flag, const QString &text) { - auto checked = (_rights.c_channelBannedRights().vflags.v & flag) != 0; + auto addCheckbox = [this](Flags flags, const QString &text) { + auto checked = (_rights.c_channelBannedRights().vflags.v & flags) == 0; auto control = addControl(object_ptr(this, text, checked, st::defaultBoxCheckbox)); - _checkboxes.emplace(flag, control); + connect(control, &Ui::Checkbox::changed, this, [this, control] { + applyDependencies(control); + }, Qt::QueuedConnection); + _checkboxes.emplace(flags, control); }; addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read)); addCheckbox(Flag::f_send_messages, lang(lng_rights_chat_send_text)); addCheckbox(Flag::f_send_media, lang(lng_rights_chat_send_media)); - addCheckbox(Flag::f_send_stickers, lang(lng_rights_chat_send_stickers)); + addCheckbox(Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline, lang(lng_rights_chat_send_stickers)); addCheckbox(Flag::f_embed_links, lang(lng_rights_chat_send_links)); + + _restrictUntil = addControl(object_ptr(this, QString(), st::boxLinkButton)); + _restrictUntil->setClickedCallback([this] { showRestrictUntil(); }); + setRestrictUntil(_until); + + //addControl(object_ptr(this, lang(lng_rights_chat_banned_block), st::boxLinkButton)); + + addButton(langFactory(lng_settings_save), [this] { + if (!_saveCallback) { + return; + } + auto newFlags = MTPDchannelBannedRights::Flags(0); + for (auto &&checkbox : _checkboxes) { + if (checkbox.second->checked()) { + newFlags &= ~checkbox.first; + } else { + newFlags |= checkbox.first; + } + } + _saveCallback(MTP_channelBannedRights(MTP_flags(newFlags), MTP_int(_until))); + }); + addButton(langFactory(lng_cancel), [this] { closeBox(); }); + + applyDependencies(nullptr); + for (auto &&checkbox : _checkboxes) { + checkbox.second->finishAnimations(); + } + resizeToContent(); } + +void EditRestrictedBox::applyDependencies(QPointer changed) { + ApplyDependencies(_checkboxes, _dependencies, changed); +} + +MTPChannelBannedRights EditRestrictedBox::DefaultRights(gsl::not_null channel) { + auto defaultRights = Flag::f_send_messages | Flag::f_send_media | Flag::f_embed_links | Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline; + return MTP_channelBannedRights(MTP_flags(defaultRights), MTP_int(0)); +} + +void EditRestrictedBox::showRestrictUntil() { + auto tomorrow = QDate::currentDate().addDays(1); + auto highlighted = isUntilForever() ? tomorrow : date(_until).date(); + auto month = highlighted; + _restrictUntilBox = Ui::show(Box(month, highlighted, [this](const QDate &date) { setRestrictUntil(static_cast(QDateTime(date).toTime_t())); }), KeepOtherLayers); + _restrictUntilBox->setMaxDate(QDate::currentDate().addDays(kMaxRestrictDelayDays)); + _restrictUntilBox->setMinDate(tomorrow); + _restrictUntilBox->addLeftButton(langFactory(lng_rights_chat_banned_forever), [this] { setRestrictUntil(0); }); +} + +void EditRestrictedBox::setRestrictUntil(int32 until) { + _until = until; + if (_restrictUntilBox) { + _restrictUntilBox->closeBox(); + } + auto untilText = [this] { + if (isUntilForever()) { + return lang(lng_rights_chat_banned_forever); + } + return langDayOfMonthFull(date(_until).date()); + }; + _restrictUntil->setText(lng_rights_chat_banned_until(lt_when, untilText())); +} diff --git a/Telegram/SourceFiles/boxes/edit_participant_box.h b/Telegram/SourceFiles/boxes/edit_participant_box.h index 9b450888f..d607bc74c 100644 --- a/Telegram/SourceFiles/boxes/edit_participant_box.h +++ b/Telegram/SourceFiles/boxes/edit_participant_box.h @@ -28,15 +28,16 @@ class LinkButton; class Checkbox; } // namespace Ui +class CalendarBox; + class EditParticipantBox : public BoxContent { public: EditParticipantBox(QWidget*, gsl::not_null channel, gsl::not_null user); protected: - void resizeToContent(); + void prepare() override; - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; + void resizeToContent(); gsl::not_null user() const { return _user; @@ -46,16 +47,14 @@ protected: } template - QPointer addControl(object_ptr row) { - _rows.push_back(std::move(row)); - return static_cast(_rows.back().data()); - } + QPointer addControl(object_ptr row); private: gsl::not_null _user; gsl::not_null _channel; - std::vector> _rows; + class Inner; + QPointer _inner; }; @@ -63,33 +62,58 @@ class EditAdminBox : public EditParticipantBox { public: EditAdminBox(QWidget*, gsl::not_null channel, gsl::not_null user, const MTPChannelAdminRights &rights, base::lambda callback); + static MTPChannelAdminRights DefaultRights(gsl::not_null channel); + protected: void prepare() override; private: using Flag = MTPDchannelAdminRights::Flag; + using Flags = MTPDchannelAdminRights::Flags; + void applyDependencies(QPointer changed); void refreshAboutAddAdminsText(); MTPChannelAdminRights _rights; + std::vector> _dependencies; + base::lambda _saveCallback; - std::map> _checkboxes; + std::map> _checkboxes; QPointer _aboutAddAdmins; }; +// Restricted box works with flags in the opposite way. +// If some flag is set in the rights then the checkbox is unchecked. + class EditRestrictedBox : public EditParticipantBox { public: EditRestrictedBox(QWidget*, gsl::not_null channel, gsl::not_null user, const MTPChannelBannedRights &rights, base::lambda callback); + static MTPChannelBannedRights DefaultRights(gsl::not_null channel); + static constexpr auto kRestrictUntilForever = TimeId(INT_MAX); + protected: void prepare() override; private: using Flag = MTPDchannelBannedRights::Flag; + using Flags = MTPDchannelBannedRights::Flags; + + void applyDependencies(QPointer changed); + void showRestrictUntil(); + void setRestrictUntil(int32 until); + bool isUntilForever() { + return (_until <= 0) || (_until == kRestrictUntilForever); + } MTPChannelBannedRights _rights; + int32 _until = 0; + std::vector> _dependencies; + base::lambda _saveCallback; - std::map> _checkboxes; + std::map> _checkboxes; + QPointer _restrictUntil; + QPointer _restrictUntilBox; }; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index e0381f444..6c6babfb9 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "boxes/contacts_box.h" #include "boxes/confirm_box.h" +#include "boxes/edit_participant_box.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" #include "ui/effects/ripple_animation.h" @@ -79,9 +80,13 @@ void MembersBox::prepare() { _inner = setInnerWidget(object_ptr(this, _channel, _filter), st::boxLayerScroll); setDimensions(st::boxWideWidth, st::boxMaxListHeight); - addButton(langFactory(lng_close), [this] { closeBox(); }); - if (_channel->amCreator() && (_channel->membersCount() < (_channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!_channel->isMegagroup() && !_channel->isPublic()) || _filter == MembersFilter::Admins)) { - addLeftButton(langFactory((_filter == MembersFilter::Admins) ? lng_channel_add_admin : lng_channel_add_members), [this] { onAdd(); }); + refreshButtons(); + if (_filter == MembersFilter::Admins) { + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged, [this](const Notify::PeerUpdate &update) { + if (update.peer == _channel) { + refreshButtons(); + } + })); } connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int))); @@ -90,6 +95,18 @@ void MembersBox::prepare() { connect(_loadTimer, SIGNAL(timeout()), _inner, SLOT(load())); } +void MembersBox::refreshButtons() { + clearButtons(); + addButton(langFactory(lng_close), [this] { closeBox(); }); + if (_filter == MembersFilter::Admins) { + if (_channel->canAddAdmins()) { + addLeftButton(langFactory(lng_channel_add_admin), [this] { onAdd(); }); + } + } else if (_channel->amCreator() && (_channel->membersCount() < (_channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!_channel->isMegagroup() && !_channel->isPublic()))) { + addLeftButton(langFactory(lng_channel_add_members), [this] { onAdd(); }); + } +} + void MembersBox::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Down) { _inner->selectSkip(1); @@ -133,11 +150,20 @@ void MembersBox::onAdminAdded() { _loadTimer->start(kReloadChannelAdminsTimeout); } -MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter filter) : TWidget(parent) +struct MembersBox::Inner::RowData { + std::unique_ptr ripple; + int rippleRowTop = 0; + Text name; + QString online; + bool onlineColor; + bool canKick; +}; + +MembersBox::Inner::Inner(QWidget *parent, gsl::not_null channel, MembersFilter filter) : TWidget(parent) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _channel(channel) , _filter(filter) -, _kickText(lang(lng_profile_kick)) +, _kickText(lang((filter == MembersFilter::Admins) ? lng_profile_edit_admin : lng_profile_kick)) , _kickWidth(st::normalFont->width(_kickText)) , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) , _about(_aboutWidth) { @@ -168,7 +194,7 @@ void MembersBox::Inner::paintEvent(QPaintEvent *e) { auto yFrom = r.y() - st::membersMarginTop; auto yTo = r.y() + r.height() - st::membersMarginTop; p.translate(0, st::membersMarginTop); - if (_rows.isEmpty()) { + if (_rows.empty()) { p.setFont(st::noContactsFont); p.setPen(st::noContactsColor); p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); @@ -179,7 +205,7 @@ void MembersBox::Inner::paintEvent(QPaintEvent *e) { for (; from < to; ++from) { auto selected = (_pressed >= 0) ? (from == _pressed) : (from == _selected); auto kickSelected = (_pressed >= 0) ? (from == _kickPressed && from == _kickSelected) : (from == _kickSelected); - paintDialog(p, ms, _rows[from], data(from), selected, kickSelected); + paintDialog(p, ms, _rows[from], selected, kickSelected); p.translate(0, _rowHeight); } if (to == _rows.size() && _filter == MembersFilter::Recent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) { @@ -213,8 +239,9 @@ void MembersBox::Inner::mousePressEvent(QMouseEvent *e) { updateSelection(); setPressed(_selected); _kickPressed = _kickSelected; - if (_selected >= 0 && _selected < _datas.size() && _kickSelected < 0) { - addRipple(_datas[_selected]); + if (_selected >= 0 && _selected < _rows.size() && _kickSelected < 0) { + ensureData(_rows[_selected]); + addRipple(_rows[_selected].data.get()); } } @@ -225,18 +252,7 @@ void MembersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { if (pressed == _selected && kickPressed == _kickSelected) { if (kickPressed >= 0) { - if (!_kickRequestId) { - _kickConfirm = _rows.at(_kickSelected); - if (_kickBox) _kickBox->deleteLater(); - auto text = (_filter == MembersFilter::Recent ? (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel) : lng_profile_sure_kick_admin)(lt_user, _kickConfirm->firstName); - _kickBox = Ui::show(Box(text, base::lambda_guarded(this, [this] { - if (_filter == MembersFilter::Recent) { -// _kickRequestId = MTP::send(MTPchannels_KickFromChannel(_channel->inputChannel, _kickConfirm->inputUser, MTP_bool(true)), rpcDone(&Inner::kickDone), rpcFail(&Inner::kickFail)); - } else { - // _kickRequestId = MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, _kickConfirm->inputUser, MTP_channelRoleEmpty()), rpcDone(&Inner::kickAdminDone), rpcFail(&Inner::kickFail)); - } - })), KeepOtherLayers); - } + actionPressed(_rows[_kickSelected]); } else if (pressed >= 0) { chooseParticipant(); } @@ -244,7 +260,42 @@ void MembersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } } -void MembersBox::Inner::addRipple(MemberData *data) { +void MembersBox::Inner::actionPressed(Member &row) { + auto user = row.user; + if (_kickBox) _kickBox->closeBox(); + if (_filter == MembersFilter::Recent) { + auto text = (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel)(lt_user, user->firstName); + _kickBox = Ui::show(Box(text, base::lambda_guarded(this, [this, user] { + MTP::send(MTPchannels_EditBanned(_channel->inputChannel, user->inputUser, ChannelData::KickedRestrictedRights()), ::rpcDone(base::lambda_guarded(this, [this, user](const MTPUpdates &result) { + App::main()->sentUpdatesReceived(result); + removeKicked(user); + if (_kickBox) _kickBox->closeBox(); + })), rpcFail(&Inner::kickFail)); + })), KeepOtherLayers); + } else { + auto currentRights = _rows[_kickSelected].adminRights; + _kickBox = Ui::show(Box(_channel, user, currentRights, base::lambda_guarded(this, [this, user](const MTPChannelAdminRights &rights) { + if (_kickBox) _kickBox->closeBox(); + MTP::send(MTPchannels_EditAdmin(_channel->inputChannel, user->inputUser, rights), ::rpcDone(base::lambda_guarded(this, [this, user, rights](const MTPUpdates &result, mtpRequestId req) { + if (App::main()) App::main()->sentUpdatesReceived(result); + _channel->applyEditAdmin(user, rights); + if (rights.c_channelAdminRights().vflags.v == 0) { + removeKicked(user); + } else { + auto it = std::find_if(_rows.begin(), _rows.end(), [this, user](auto &&row) { + return (row.user == user); + }); + if (it != _rows.end()) { + it->adminRights = rights; + } + } + if (_kickBox) _kickBox->closeBox(); + })), rpcFail(&Inner::kickFail)); + })), KeepOtherLayers); + } +} + +void MembersBox::Inner::addRipple(gsl::not_null data) { auto rowTop = getSelectedRowTop(); if (!data->ripple) { auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _rowHeight)); @@ -256,21 +307,26 @@ void MembersBox::Inner::addRipple(MemberData *data) { data->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, rowTop)); } -void MembersBox::Inner::stopLastRipple(MemberData *data) { +void MembersBox::Inner::stopLastRipple(gsl::not_null data) { if (data->ripple) { data->ripple->lastStop(); } } void MembersBox::Inner::setPressed(int pressed) { - if (_pressed >= 0 && _pressed < _datas.size()) { - stopLastRipple(_datas[_pressed]); + if (_pressed >= 0 && _pressed < _rows.size()) { + if (_rows[_pressed].data) { + stopLastRipple(_rows[_pressed].data.get()); + } } _pressed = pressed; } -void MembersBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, MemberData *data, bool selected, bool kickSelected) { - UserData *user = peer->asUser(); +void MembersBox::Inner::paintDialog(Painter &p, TimeMs ms, Member &row, bool selected, bool kickSelected) { + ensureData(row); + + auto user = row.user; + auto &data = row.data; p.fillRect(0, 0, width(), _rowHeight, selected ? st::contactsBgOver : st::contactsBg); if (data->ripple) { @@ -279,13 +335,13 @@ void MembersBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, Membe data->ripple.reset(); } } - peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); + user->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); p.setPen(st::contactsNameFg); - int32 namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - int32 namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0); - if (peer->isVerified()) { + auto namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); + auto namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0); + if (user->isVerified()) { auto icon = &st::dialogsVerifiedIcon; namew -= icon->width(); icon->paint(p, namex + qMin(data->name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width()); @@ -313,7 +369,7 @@ void MembersBox::Inner::selectSkip(int32 dir) { } cur += dir; if (cur <= 0) { - _selected = _rows.isEmpty() ? -1 : 0; + _selected = _rows.empty() ? -1 : 0; } else if (cur >= _rows.size()) { _selected = -1; } else { @@ -324,7 +380,7 @@ void MembersBox::Inner::selectSkip(int32 dir) { _selected = -1; } } else { - if (!_rows.isEmpty()) { + if (!_rows.empty()) { if (_selected < 0) _selected = _rows.size() - 1; } } @@ -341,9 +397,14 @@ void MembersBox::Inner::selectSkipPage(int32 h, int32 dir) { selectSkip(points * dir); } -MembersBox::Inner::MemberData::MemberData() = default; +MembersBox::Inner::Member::Member(gsl::not_null user) : user(user) { +} -MembersBox::Inner::MemberData::~MemberData() = default; +MembersBox::Inner::Member::Member(Member &&other) = default; + +MembersBox::Inner::Member &MembersBox::Inner::Member::operator=(Member &&other) = default; + +MembersBox::Inner::Member::~Member() = default; void MembersBox::Inner::loadProfilePhotos() { if (_visibleTop >= _visibleBottom) return; @@ -355,7 +416,7 @@ void MembersBox::Inner::loadProfilePhotos() { if (yTo < 0) return; if (yFrom < 0) yFrom = 0; - if (!_rows.isEmpty()) { + if (!_rows.empty()) { int32 from = yFrom / _rowHeight; if (from < 0) from = 0; if (from < _rows.size()) { @@ -363,7 +424,7 @@ void MembersBox::Inner::loadProfilePhotos() { if (to > _rows.size()) to = _rows.size(); for (; from < to; ++from) { - _rows[from]->loadUserpic(); + _rows[from].user->loadUserpic(); } } } @@ -371,14 +432,14 @@ void MembersBox::Inner::loadProfilePhotos() { void MembersBox::Inner::chooseParticipant() { if (_selected < 0 || _selected >= _rows.size()) return; - if (auto peer = _rows[_selected]) { + if (auto peer = _rows[_selected].user) { Ui::hideLayer(); Ui::showPeerProfile(peer); } } void MembersBox::Inner::refresh() { - if (_rows.isEmpty()) { + if (_rows.empty()) { resize(width(), st::membersMarginTop + st::noContactsHeight + st::membersMarginBottom); _aboutHeight = 0; } else { @@ -402,10 +463,8 @@ MembersFilter MembersBox::Inner::filter() const { MembersAlreadyIn MembersBox::Inner::already() const { MembersAlreadyIn result; - for_const (auto peer, _rows) { - if (peer->isUser()) { - result.insert(peer->asUser()); - } + for_const (auto &&row, _rows) { + result.insert(row.user); } return result; } @@ -423,34 +482,27 @@ void MembersBox::Inner::clearSel() { updateSelection(); } -MembersBox::Inner::MemberData *MembersBox::Inner::data(int32 index) { - if (MemberData *result = _datas.at(index)) { - return result; +void MembersBox::Inner::ensureData(Member &row) { + if (row.data) { + return; } - MemberData *result = _datas[index] = new MemberData(); - result->name.setText(st::contactsNameStyle, _rows[index]->name, _textNameOptions); - int32 t = unixtime(); - result->online = App::onlineText(_rows[index], t);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); - result->onlineColor = App::onlineColorUse(_rows[index], t); + row.data = std::make_unique(); + row.data->name.setText(st::contactsNameStyle, row.user->name, _textNameOptions); + auto now = unixtime(); + row.data->online = App::onlineText(row.user, now);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); + row.data->onlineColor = App::onlineColorUse(row.user, now); if (_filter == MembersFilter::Recent) { - result->canKick = _channel->canBanMembers() ? (_roles[index] == MemberRole::None) : false; + row.data->canKick = _channel->canBanMembers() ? (row.role == MemberRole::None) : false; } else if (_filter == MembersFilter::Admins) { - result->canKick = _channel->amCreator() ? (_roles[index] == MemberRole::Admin) : false; + row.data->canKick = _channel->amCreator() ? (row.role == MemberRole::Admin) : row.adminCanEdit; } else { - result->canKick = false; + row.data->canKick = false; } - return result; } void MembersBox::Inner::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(); + if (_kickBox) _kickBox->closeBox(); clearSel(); } @@ -461,12 +513,15 @@ MembersBox::Inner::~Inner() { void MembersBox::Inner::updateSelection() { if (!_mouseSelection) return; - QPoint p(mapFromGlobal(_lastMousePos)); + auto p = mapFromGlobal(_lastMousePos); p.setY(p.y() - st::membersMarginTop); - bool in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); + auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); auto selected = (in && p.y() >= 0 && p.y() < _rows.size() * _rowHeight) ? (p.y() / _rowHeight) : -1; auto kickSelected = selected; - if (selected >= 0 && (!data(selected)->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), selected * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { + if (selected >= 0) { + ensureData(_rows[selected]); + } + if (selected >= 0 && (!_rows[selected].data->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), selected * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { kickSelected = -1; } if (_selected != selected || _kickSelected != kickSelected) { @@ -501,14 +556,14 @@ void MembersBox::Inner::updateSelectedRow() { } void MembersBox::Inner::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::contactsNameStyle, peer->name, _textNameOptions); + for (auto i= 0, l = int(_rows.size()); i != l; ++i) { + auto &row = _rows[i]; + if (row.user == peer) { + if (row.data) { + row.data->name.setText(st::contactsNameStyle, peer->name, _textNameOptions); update(0, st::membersMarginTop + i * _rowHeight, width(), _rowHeight); - } else { - break; } + break; } } } @@ -522,9 +577,6 @@ void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &r auto &d = result.c_channels_channelParticipants(); auto &v = d.vparticipants.v; _rows.reserve(v.size()); - _datas.reserve(v.size()); - _dates.reserve(v.size()); - _roles.reserve(v.size()); if (_filter == MembersFilter::Recent && _channel->membersCount() < d.vcount.v) { _channel->setMembersCount(d.vcount.v); @@ -535,11 +587,15 @@ void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &r } App::feedUsers(d.vusers); - auto emptyRights = MTP_channelAdminRights(MTP_flags(0)); - for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { - int32 userId = 0, addedTime = 0; + auto emptyAdminRights = MTP_channelAdminRights(MTP_flags(0)); + auto emptyRestrictedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); + for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) { + auto userId = UserId(0); + auto addedTime = TimeId(0); auto role = MemberRole::None; - auto rights = emptyRights; + auto adminCanEdit = false; + auto adminRights = emptyAdminRights; + auto restrictedRights = emptyRestrictedRights; switch (i->type()) { case mtpc_channelParticipant: userId = i->c_channelParticipant().vuser_id.v; @@ -554,7 +610,8 @@ void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &r role = MemberRole::Admin; userId = i->c_channelParticipantAdmin().vuser_id.v; addedTime = i->c_channelParticipantAdmin().vdate.v; - rights = i->c_channelParticipantAdmin().vadmin_rights; + adminRights = i->c_channelParticipantAdmin().vadmin_rights; + adminCanEdit = i->c_channelParticipantAdmin().is_can_edit(); break; case mtpc_channelParticipantCreator: userId = i->c_channelParticipantCreator().vuser_id.v; @@ -564,34 +621,42 @@ void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &r case mtpc_channelParticipantBanned: userId = i->c_channelParticipantBanned().vuser_id.v; addedTime = i->c_channelParticipantBanned().vdate.v; - role = MemberRole::Kicked; + restrictedRights = i->c_channelParticipantBanned().vbanned_rights; + role = MemberRole::Restricted; } if (auto user = App::userLoaded(userId)) { - _rows.push_back(user); - _dates.push_back(date(addedTime)); - _roles.push_back(role); - _adminRights.push_back(rights); - _datas.push_back(nullptr); + auto row = Member(user); + row.adminCanEdit = adminCanEdit; + row.adminRights = adminRights; + row.restrictedRights = restrictedRights; + row.date = date(addedTime); + row.role = role; + _rows.push_back(std::move(row)); + if (role == MemberRole::Creator && _channel->mgInfo) { + _channel->mgInfo->creator = user; + } } } // update admins if we got all of them if (_filter == MembersFilter::Admins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) { _channel->mgInfo->lastAdmins.clear(); - for (int32 i = 0, l = _rows.size(); i != l; ++i) { - if (_roles[i] == MemberRole::Admin) { - _channel->mgInfo->lastAdmins.insert(_rows[i], _adminRights[i]); + for (auto &&row : _rows) { + if (row.role == MemberRole::Admin) { + _channel->mgInfo->lastAdmins.insert(row.user, MegagroupInfo::Admin { row.adminRights, row.adminCanEdit }); } } Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged); } - if (_rows.isEmpty()) { - _rows.push_back(App::self()); - _dates.push_back(date(MTP_int(_channel->date))); - _roles.push_back(MemberRole::Self); - _datas.push_back(nullptr); + if (_rows.empty()) { + auto row = Member(App::self()); + row.date = date(MTP_int(_channel->date)); + row.role = MemberRole::Self; + row.adminRights = _channel->adminRightsBoxed(); + row.restrictedRights = _channel->restrictedRightsBoxed(); + _rows.push_back(std::move(row)); } clearSel(); @@ -608,22 +673,7 @@ bool MembersBox::Inner::membersFailed(const RPCError &error, mtpRequestId req) { return true; } -void MembersBox::Inner::kickDone(const MTPUpdates &result, mtpRequestId req) { - App::main()->sentUpdatesReceived(result); - - if (_kickRequestId != req) return; - removeKicked(); - if (_kickBox) _kickBox->closeBox(); -} - -void MembersBox::Inner::kickAdminDone(const MTPUpdates &result, mtpRequestId req) { - if (_kickRequestId != req) return; - if (App::main()) App::main()->sentUpdatesReceived(result); - removeKicked(); - if (_kickBox) _kickBox->closeBox(); -} - -bool MembersBox::Inner::kickFail(const RPCError &error, mtpRequestId req) { +bool MembersBox::Inner::kickFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; if (_kickBox) _kickBox->closeBox(); @@ -631,15 +681,12 @@ bool MembersBox::Inner::kickFail(const RPCError &error, mtpRequestId req) { return true; } -void MembersBox::Inner::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); +void MembersBox::Inner::removeKicked(UserData *kicked) { + auto it = std::find_if(_rows.begin(), _rows.end(), [this, kicked](auto &&row) { + return (row.user == kicked); + }); + if (it != _rows.end()) { + _rows.erase(it); clearSel(); if (_filter == MembersFilter::Recent && _channel->membersCount() > 1) { _channel->setMembersCount(_channel->membersCount() - 1); @@ -650,5 +697,4 @@ void MembersBox::Inner::removeKicked() { } refresh(); } - _kickConfirm = 0; } diff --git a/Telegram/SourceFiles/boxes/members_box.h b/Telegram/SourceFiles/boxes/members_box.h index 359b6cbaa..5327cd8e8 100644 --- a/Telegram/SourceFiles/boxes/members_box.h +++ b/Telegram/SourceFiles/boxes/members_box.h @@ -68,6 +68,7 @@ protected: private: void onAdd(); + void refreshButtons(); ChannelData *_channel = nullptr; MembersFilter _filter = MembersFilter::Recent; @@ -86,13 +87,11 @@ class MembersBox::Inner : public TWidget, public RPCSender, private base::Subscr Q_OBJECT public: - Inner(QWidget *parent, ChannelData *channel, MembersFilter filter); + Inner(QWidget *parent, gsl::not_null channel, MembersFilter filter); void selectSkip(int32 dir); void selectSkipPage(int32 h, int32 dir); - void chooseParticipant(); - void refresh(); ChannelData *channel() const; @@ -127,20 +126,34 @@ protected: void mouseReleaseEvent(QMouseEvent *e) override; private: - struct MemberData { - MemberData(); - ~MemberData(); - - std::unique_ptr ripple; - int rippleRowTop = 0; - Text name; - QString online; - bool onlineColor; - bool canKick; + struct RowData; + enum class MemberRole { + None, + Self, + Creator, + Admin, + Restricted, + Kicked, }; - void addRipple(MemberData *data); - void stopLastRipple(MemberData *data); + struct Member { + Member(gsl::not_null user); + Member(Member &&other); + Member &operator=(Member &&other); + ~Member(); + + gsl::not_null user; + QDateTime date; + MemberRole role = MemberRole::None; + bool adminCanEdit = false; + MTPChannelAdminRights adminRights; + MTPChannelBannedRights restrictedRights; + std::unique_ptr data; + }; + void addRipple(gsl::not_null data); + void stopLastRipple(gsl::not_null data); void setPressed(int pressed); + void chooseParticipant(); + void actionPressed(Member &row); void updateSelection(); void loadProfilePhotos(); @@ -148,17 +161,15 @@ private: void updateRowWithTop(int rowTop); int getSelectedRowTop() const; void updateSelectedRow(); - MemberData *data(int32 index); + void ensureData(Member &row); - void paintDialog(Painter &p, TimeMs ms, PeerData *peer, MemberData *data, bool selected, bool kickSelected); + void paintDialog(Painter &p, TimeMs ms, Member &row, bool selected, bool kickSelected); 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 MTPUpdates &result, mtpRequestId req); - bool kickFail(const RPCError &error, mtpRequestId req); - void removeKicked(); + bool kickFail(const RPCError &error); + void removeKicked(UserData *kicked); void clear(); @@ -166,7 +177,7 @@ private: int _visibleTop = 0; int _visibleBottom = 0; - ChannelData *_channel = nullptr; + gsl::not_null _channel; MembersFilter _filter; QString _kickText; @@ -179,26 +190,11 @@ private: int _kickPressed = -1; bool _mouseSelection = false; - UserData *_kickConfirm = nullptr; - mtpRequestId _kickRequestId = 0; - - QPointer _kickBox; - - enum class MemberRole { - None, - Self, - Creator, - Admin, - Kicked, - }; + QPointer _kickBox; bool _loading = true; mtpRequestId _loadingRequestId = 0; - QVector _rows; - QVector _dates; - QVector _roles; - QVector _adminRights; - QVector _datas; + std::vector _rows; int _aboutWidth = 0; Text _about; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c700e23ee..1ee498cc3 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -900,9 +900,9 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction, if (peer->isMegagroup()) { if (auto user = App::userLoaded(uid)) { auto channel = peer->asChannel(); - auto megagroupInfo = channel->mgInfo; + auto &megagroupInfo = channel->mgInfo; - int32 index = megagroupInfo->lastParticipants.indexOf(user); + auto index = megagroupInfo->lastParticipants.indexOf(user); if (index >= 0) { megagroupInfo->lastParticipants.removeAt(index); Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged); @@ -1117,20 +1117,24 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { adding->addToOverview(AddToOverviewNew); if (adding->from()->id) { - if (adding->from()->isUser()) { - QList *lastAuthors = 0; - if (peer->isChat()) { - lastAuthors = &peer->asChat()->lastAuthors; - } else if (peer->isMegagroup()) { - lastAuthors = &peer->asChannel()->mgInfo->lastParticipants; + if (auto user = adding->from()->asUser()) { + auto getLastAuthors = [this]() -> QList>* { + if (auto chat = peer->asChat()) { + return &chat->lastAuthors; + } else if (auto channel = peer->asMegagroup()) { + return &channel->mgInfo->lastParticipants; + } + return nullptr; + }; + if (auto channel = peer->asMegagroup()) { if (adding->from()->asUser()->botInfo) { - peer->asChannel()->mgInfo->bots.insert(adding->from()->asUser()); - if (peer->asChannel()->mgInfo->botStatus != 0 && peer->asChannel()->mgInfo->botStatus < 2) { - peer->asChannel()->mgInfo->botStatus = 2; + channel->mgInfo->bots.insert(adding->from()->asUser()); + if (channel->mgInfo->botStatus != 0 && channel->mgInfo->botStatus < 2) { + channel->mgInfo->botStatus = 2; } } } - if (lastAuthors) { + if (auto lastAuthors = getLastAuthors()) { int prev = lastAuthors->indexOf(adding->from()->asUser()); if (prev > 0) { lastAuthors->removeAt(prev); @@ -1146,15 +1150,17 @@ HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { } } if (adding->definesReplyKeyboard()) { - MTPDreplyKeyboardMarkup::Flags markupFlags = adding->replyKeyboardFlags(); + auto markupFlags = adding->replyKeyboardFlags(); if (!(markupFlags & MTPDreplyKeyboardMarkup::Flag::f_selective) || adding->mentionsMe()) { - OrderedSet *markupSenders = 0; - if (peer->isChat()) { - markupSenders = &peer->asChat()->markupSenders; - } else if (peer->isMegagroup()) { - markupSenders = &peer->asChannel()->mgInfo->markupSenders; - } - if (markupSenders) { + auto getMarkupSenders = [this]() -> OrderedSet>* { + if (auto chat = peer->asChat()) { + return &chat->markupSenders; + } else if (auto channel = peer->asMegagroup()) { + return &channel->mgInfo->markupSenders; + } + return nullptr; + }; + if (auto markupSenders = getMarkupSenders()) { markupSenders->insert(adding->from()); } if (markupFlags & MTPDreplyKeyboardMarkup_ClientFlag::f_zero) { // zero markup means replyKeyboardHide @@ -1299,8 +1305,8 @@ void History::addOlderSlice(const QVector &slice) { } else if (loadedAtBottom()) { // add photos to overview and authors to lastAuthors bool channel = isChannel(); int32 mask = 0; - QList *lastAuthors = nullptr; - OrderedSet *markupSenders = nullptr; + QList> *lastAuthors = nullptr; + OrderedSet> *markupSenders = nullptr; if (peer->isChat()) { lastAuthors = &peer->asChat()->lastAuthors; markupSenders = &peer->asChat()->markupSenders; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 592cca1fe..387ff0f7e 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -795,11 +795,18 @@ bool HistoryItem::canEdit(const QDateTime &cur) const { return false; } } - if (out() || messageToMyself) { + if (messageToMyself) { return true; } if (auto channel = _history->peer->asChannel()) { - return channel->canDeleteMessages(); + if (channel->canEditMessages()) { + return true; + } + if (out()) { + return !isPost() || channel->canPublish(); + } + } else { + return out(); } } return false; @@ -814,16 +821,13 @@ bool HistoryItem::canDelete() const { if (id == 1) { return false; } - if (channel->amCreator()) { + if (channel->canDeleteMessages()) { return true; } - if (isPost()) { - if (out()) { - return true; - } - return false; + if (out() && toHistoryMessage()) { + return isPost() ? channel->canPublish() : true; } - return (channel->canDeleteMessages() || out()); + return false; } bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const { @@ -857,10 +861,11 @@ bool HistoryItem::canDeleteForEveryone(const QDateTime &cur) const { bool HistoryItem::suggestBanReport() const { auto channel = history()->peer->asChannel(); - if (!channel || !channel->canBanMembers()) { + auto fromUser = from()->asUser(); + if (!channel || !fromUser || !channel->canRestrictUser(fromUser)) { return false; } - return !isPost() && !out() && from()->isUser() && toHistoryMessage(); + return !isPost() && !out() && toHistoryMessage(); } bool HistoryItem::suggestDeleteAllReport() const { diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index fd0ff9c24..71f939742 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -4111,7 +4111,7 @@ void HistoryWidget::updateOnlineDisplayTimer() { ChatData *chat = _peer->asChat(); if (chat->participants.isEmpty()) return; - for (ChatData::Participants::const_iterator i = chat->participants.cbegin(), e = chat->participants.cend(); i != e; ++i) { + for (auto i = chat->participants.cbegin(), e = chat->participants.cend(); i != e; ++i) { int32 onlineWillChangeIn = App::onlineWillChangeIn(i.key(), t); if (onlineWillChangeIn < minIn) { minIn = onlineWillChangeIn; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index c0baa169d..7d684a0a8 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1226,7 +1226,7 @@ void MainWidget::clearHistory(PeerData *peer) { MTP::send(MTPmessages_DeleteHistory(MTP_flags(flags), peer->input, MTP_int(0)), rpcDone(&MainWidget::deleteHistoryPart, request)); } -void MainWidget::addParticipants(PeerData *chatOrChannel, const QVector &users) { +void MainWidget::addParticipants(PeerData *chatOrChannel, const std::vector> &users) { if (chatOrChannel->isChat()) { auto chat = chatOrChannel->asChat(); for_const (auto user, users) { @@ -1234,8 +1234,8 @@ void MainWidget::addParticipants(PeerData *chatOrChannel, const QVectorisChannel()) { QVector inputUsers; - inputUsers.reserve(qMin(users.size(), int(MaxUsersPerInvite))); - for (QVector::const_iterator i = users.cbegin(), e = users.cend(); i != e; ++i) { + inputUsers.reserve(qMin(int(users.size()), int(MaxUsersPerInvite))); + for (auto i = users.cbegin(), e = users.cend(); i != e; ++i) { inputUsers.push_back((*i)->inputUser); if (inputUsers.size() == MaxUsersPerInvite) { MTP::send(MTPchannels_InviteToChannel(chatOrChannel->asChannel()->inputChannel, MTP_vector(inputUsers)), rpcDone(&MainWidget::inviteToChannelDone, chatOrChannel->asChannel()), rpcFail(&MainWidget::addParticipantsFail, chatOrChannel->asChannel()), 0, 5); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index aa3f8e85f..fc1c38958 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -273,7 +273,7 @@ public: void clearHistory(PeerData *peer); void deleteAllFromUser(ChannelData *channel, UserData *from); - void addParticipants(PeerData *chatOrChannel, const QVector &users); + void addParticipants(PeerData *chatOrChannel, const std::vector> &users); struct UserAndPeer { UserData *user; PeerData *peer; diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index d683bfe1b..3cdc47e7b 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -34,6 +34,8 @@ struct PeerUpdate { PeerData *peer; enum class Flag { + None = 0x00000000U, + // Common flags NameChanged = 0x00000001U, UsernameChanged = 0x00000002U, @@ -66,11 +68,7 @@ struct PeerUpdate { // For channels ChannelAmIn = 0x00010000U, - ChannelAmEditor = 0x00020000U, - ChannelCanEditInformation = 0x00040000U, - ChannelCanAddMembers = 0x00080000U, - ChannelCanViewAdmins = 0x00100000U, - ChannelCanViewMembers = 0x00200000U, + ChannelRightsChanged = 0x00020000U, }; Q_DECLARE_FLAGS(Flags, Flag); Flags flags = 0; diff --git a/Telegram/SourceFiles/profile/profile_block_channel_members.cpp b/Telegram/SourceFiles/profile/profile_block_channel_members.cpp index bfd1c0292..f3de98c94 100644 --- a/Telegram/SourceFiles/profile/profile_block_channel_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_channel_members.cpp @@ -31,8 +31,7 @@ namespace Profile { using UpdateFlag = Notify::PeerUpdate::Flag; ChannelMembersWidget::ChannelMembersWidget(QWidget *parent, PeerData *peer) : BlockWidget(parent, peer, lang(lng_profile_participants_section)) { - auto observeEvents = UpdateFlag::ChannelCanViewAdmins - | UpdateFlag::ChannelCanViewMembers + auto observeEvents = UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged | UpdateFlag::MembersChanged; subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { @@ -47,10 +46,10 @@ void ChannelMembersWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { return; } - if (update.flags & (UpdateFlag::ChannelCanViewAdmins | UpdateFlag::AdminsChanged)) { + if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged)) { refreshAdmins(); } - if (update.flags & (UpdateFlag::ChannelCanViewMembers | UpdateFlag::MembersChanged)) { + if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::MembersChanged)) { refreshMembers(); } refreshVisibility(); diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index f86d690af..27814d9e8 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -67,41 +67,35 @@ GroupMembersWidget::GroupMembersWidget(QWidget *parent, PeerData *peer, TitleVis refreshMembers(); } -void GroupMembersWidget::addAdmin(gsl::not_null user) { +void GroupMembersWidget::editAdmin(gsl::not_null user) { auto megagroup = peer()->asMegagroup(); if (!megagroup) { return; // not supported } - auto currentRights = megagroup->mgInfo->lastAdmins.value(user, MTP_channelAdminRights(MTP_flags(0))); + auto defaultAdmin = MegagroupInfo::Admin { EditAdminBox::DefaultRights(megagroup) }; + auto currentRights = megagroup->mgInfo->lastAdmins.value(user, defaultAdmin).rights; Ui::show(Box(megagroup, user, currentRights, base::lambda_guarded(this, [this, megagroup, user](const MTPChannelAdminRights &rights) { Ui::hideLayer(); MTP::send(MTPchannels_EditAdmin(megagroup->inputChannel, user->inputUser, rights), rpcDone(base::lambda_guarded(this, [this, megagroup, user, rights](const MTPUpdates &result) { if (App::main()) App::main()->sentUpdatesReceived(result); - megagroup->mgInfo->lastAdmins.insert(user, rights); - megagroup->setAdminsCount(megagroup->adminsCount() + 1); - if (App::main()) emit App::main()->peerUpdated(megagroup); - Notify::peerUpdatedDelayed(megagroup, Notify::PeerUpdate::Flag::AdminsChanged); + megagroup->applyEditAdmin(user, rights); }))); }))); } -void GroupMembersWidget::removeAdmin(gsl::not_null user) { - auto text = lng_profile_sure_kick_admin(lt_user, user->firstName); - Ui::show(Box(text, lang(lng_box_remove), base::lambda_guarded(this, [this, user] { +void GroupMembersWidget::restrictUser(gsl::not_null user) { + auto megagroup = peer()->asMegagroup(); + if (!megagroup) { + return; // not supported + } + auto defaultRestricted = MegagroupInfo::Restricted { EditRestrictedBox::DefaultRights(megagroup) }; + auto currentRights = megagroup->mgInfo->lastRestricted.value(user, defaultRestricted).rights; + Ui::show(Box(megagroup, user, currentRights, base::lambda_guarded(this, [this, megagroup, user](const MTPChannelBannedRights &rights) { Ui::hideLayer(); - if (auto chat = peer()->asChat()) { - // not supported - } else if (auto channel = peer()->asMegagroup()) { - MTP::send(MTPchannels_EditAdmin(channel->inputChannel, user->inputUser, MTP_channelAdminRights(MTP_flags(0))), rpcDone(base::lambda_guarded(this, [this, channel, user](const MTPUpdates &result) { - if (App::main()) App::main()->sentUpdatesReceived(result); - channel->mgInfo->lastAdmins.remove(user); - if (channel->adminsCount() > 1) { - channel->setAdminsCount(channel->adminsCount() - 1); - if (App::main()) emit App::main()->peerUpdated(channel); - } - Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::AdminsChanged); - }))); - } + MTP::send(MTPchannels_EditBanned(megagroup->inputChannel, user->inputUser, rights), rpcDone(base::lambda_guarded(this, [this, megagroup, user, rights](const MTPUpdates &result) { + if (App::main()) App::main()->sentUpdatesReceived(result); + megagroup->applyEditBanned(user, rights); + }))); }))); } @@ -163,7 +157,7 @@ void GroupMembersWidget::refreshUserOnline(UserData *user) { void GroupMembersWidget::preloadMore() { if (auto megagroup = peer()->asMegagroup()) { - auto megagroupInfo = megagroup->mgInfo; + auto &megagroupInfo = megagroup->mgInfo; if (!megagroupInfo->lastParticipants.isEmpty() && megagroupInfo->lastParticipants.size() < megagroup->membersCount()) { App::api()->requestLastParticipants(megagroup, false); } @@ -222,16 +216,22 @@ Ui::PopupMenu *GroupMembersWidget::fillPeerMenu(PeerData *selectedPeer) { } return false; }; - if (channel && channel->amCreator() && !item->hasAdminStar) { - result->addAction(lang(lng_context_promote_admin), base::lambda_guarded(this, [this, user] { - addAdmin(user); - })); - } else if (canRemoveAdmin()) { - result->addAction(lang(lng_context_remove_admin), base::lambda_guarded(this, [this, user] { - removeAdmin(user); - })); - } - if (item->hasRemoveLink) { + if (channel) { + if (channel->canEditAdmin(user)) { + auto label = lang(item->hasAdminStar ? lng_context_edit_permissions : lng_context_promote_admin); + result->addAction(label, base::lambda_guarded(this, [this, user] { + editAdmin(user); + })); + } + if (channel->canRestrictUser(user)) { + result->addAction(lang(lng_context_restrict_user), base::lambda_guarded(this, [this, user] { + restrictUser(user); + })); + result->addAction(lang(lng_context_remove_from_group), base::lambda_guarded(this, [this, selectedPeer] { + removePeer(selectedPeer); + })); + } + } else if (item->hasRemoveLink) { result->addAction(lang(lng_context_remove_from_group), base::lambda_guarded(this, [this, selectedPeer] { removePeer(selectedPeer); })); @@ -280,8 +280,7 @@ void GroupMembersWidget::refreshMembers() { fillChatMembers(chat); refreshLimitReached(); } else if (auto megagroup = peer()->asMegagroup()) { - checkSelfAdmin(megagroup); - auto megagroupInfo = megagroup->mgInfo; + auto &megagroupInfo = megagroup->mgInfo; if (megagroupInfo->lastParticipants.isEmpty() || megagroup->lastParticipantsCountOutdated()) { App::api()->requestLastParticipants(megagroup); } @@ -324,18 +323,6 @@ void GroupMembersWidget::checkSelfAdmin(ChatData *chat) { } } -void GroupMembersWidget::checkSelfAdmin(ChannelData *megagroup) { - if (megagroup->mgInfo->lastParticipants.isEmpty()) return; - - auto amAdmin = megagroup->hasAdminRights(); - auto self = App::self(); - if (amAdmin && !megagroup->mgInfo->lastAdmins.contains(self)) { - megagroup->selfAdminUpdated(); - } else if (!amAdmin && megagroup->mgInfo->lastAdmins.contains(self)) { - megagroup->selfAdminUpdated(); - } -} - void GroupMembersWidget::sortMembers() { if (!_sortByOnline || !itemsCount()) return; @@ -470,11 +457,14 @@ bool GroupMembersWidget::addUsersToEnd(ChannelData *megagroup) { void GroupMembersWidget::setItemFlags(Item *item, ChannelData *megagroup) { auto amCreatorOrAdmin = item->peer->isSelf() && (megagroup->hasAdminRights() || megagroup->amCreator()); - auto isAdmin = megagroup->mgInfo->lastAdmins.contains(getMember(item)->user()); - item->hasAdminStar = amCreatorOrAdmin || isAdmin; + auto adminIt = megagroup->mgInfo->lastAdmins.constFind(getMember(item)->user()); + auto isAdmin = (adminIt != megagroup->mgInfo->lastAdmins.cend()); + auto isCreator = megagroup->mgInfo->creator == item->peer; + auto adminCanEdit = isAdmin && adminIt->canEdit; + item->hasAdminStar = amCreatorOrAdmin || isAdmin || isCreator; if (item->peer->isSelf()) { item->hasRemoveLink = false; - } else if (megagroup->amCreator() || (megagroup->canBanMembers() && !item->hasAdminStar)) { + } else if (megagroup->amCreator() || (megagroup->canBanMembers() && (!item->hasAdminStar || adminCanEdit))) { item->hasRemoveLink = true; } else { item->hasRemoveLink = false; diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.h b/Telegram/SourceFiles/profile/profile_block_group_members.h index b2cd1d90e..c1b993cc6 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.h +++ b/Telegram/SourceFiles/profile/profile_block_group_members.h @@ -67,8 +67,8 @@ private: // Observed notifications. void notifyPeerUpdated(const Notify::PeerUpdate &update); - void addAdmin(gsl::not_null user); - void removeAdmin(gsl::not_null user); + void editAdmin(gsl::not_null user); + void restrictUser(gsl::not_null user); void removePeer(PeerData *selectedPeer); void refreshMembers(); void fillChatMembers(ChatData *chat); @@ -76,7 +76,6 @@ private: void sortMembers(); void updateOnlineCount(); void checkSelfAdmin(ChatData *chat); - void checkSelfAdmin(ChannelData *megagroup); void refreshLimitReached(); void preloadMore(); diff --git a/Telegram/SourceFiles/profile/profile_block_settings.cpp b/Telegram/SourceFiles/profile/profile_block_settings.cpp index 9f6733e49..b8d1df2c6 100644 --- a/Telegram/SourceFiles/profile/profile_block_settings.cpp +++ b/Telegram/SourceFiles/profile/profile_block_settings.cpp @@ -172,10 +172,7 @@ SettingsWidget::SettingsWidget(QWidget *parent, PeerData *peer) : BlockWidget(pa observeEvents |= UpdateFlag::ChatCanEdit | UpdateFlag::InviteLinkChanged; } } else if (auto channel = peer->asChannel()) { - if (channel->amCreator()) { - observeEvents |= UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged; - } - observeEvents |= UpdateFlag::ChannelAmEditor | UpdateFlag::BlockedUsersChanged; + observeEvents |= UpdateFlag::ChannelRightsChanged | UpdateFlag::BlockedUsersChanged | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged; } subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { notifyPeerUpdated(update); @@ -195,13 +192,13 @@ void SettingsWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) { if (update.flags & UpdateFlag::NotificationsEnabled) { refreshEnableNotifications(); } - if (update.flags & (UpdateFlag::ChatCanEdit | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged)) { + if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::ChatCanEdit | UpdateFlag::UsernameChanged | UpdateFlag::InviteLinkChanged)) { refreshInviteLinkButton(); } - if (update.flags & (UpdateFlag::ChatCanEdit)) { + if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::ChatCanEdit)) { refreshManageAdminsButton(); } - if ((update.flags & UpdateFlag::ChannelAmEditor) || (update.flags & UpdateFlag::BlockedUsersChanged)) { + if (update.flags & (UpdateFlag::ChannelRightsChanged | UpdateFlag::BlockedUsersChanged)) { refreshManageBlockedUsersButton(); } @@ -254,7 +251,7 @@ void SettingsWidget::refreshManageAdminsButton() { if (auto chat = peer()->asChat()) { return (chat->amCreator() && chat->canEdit()); } else if (auto channel = peer()->asMegagroup()) { - return channel->amCreator(); + return channel->hasAdminRights() || channel->amCreator(); } return false; }; @@ -269,7 +266,7 @@ void SettingsWidget::refreshManageAdminsButton() { void SettingsWidget::refreshManageBlockedUsersButton() { auto hasManageBlockedUsers = [this] { if (auto channel = peer()->asMegagroup()) { - return channel->canBanMembers() && (channel->kickedCount() > 0); + return channel->canBanMembers() && (channel->kickedCount() > 0 || channel->restrictedCount() > 0); } return false; }; @@ -288,7 +285,7 @@ void SettingsWidget::refreshInviteLinkButton() { return lang(chat->inviteLink().isEmpty() ? lng_group_invite_create : lng_group_invite_create_new); } } else if (auto channel = peer()->asChannel()) { - if (channel->amCreator() && !channel->isPublic()) { + if (channel->canHaveInviteLink() && !channel->isPublic()) { return lang(channel->inviteLink().isEmpty() ? lng_group_invite_create : lng_group_invite_create_new); } } diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index b74145b68..434f0a8de 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -45,8 +45,7 @@ using UpdateFlag = Notify::PeerUpdate::Flag; const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact | UpdateFlag::BotCanAddToGroups | UpdateFlag::ChatCanEdit - | UpdateFlag::ChannelCanEditInformation - | UpdateFlag::ChannelCanAddMembers + | UpdateFlag::ChannelRightsChanged | UpdateFlag::ChannelAmIn; } // namespace @@ -437,7 +436,7 @@ void CoverWidget::setChatButtons() { void CoverWidget::setMegagroupButtons() { if (_peerMegagroup->amIn()) { - if (_peerMegagroup->canEditInformation()) { + if (canEditPhoto()) { addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto())); } } else { @@ -449,7 +448,7 @@ void CoverWidget::setMegagroupButtons() { } void CoverWidget::setChannelButtons() { - if (_peerChannel->amCreator()) { + if (canEditPhoto()) { addButton(langFactory(lng_profile_set_group_photo), SLOT(onSetPhoto())); } else if (_peerChannel->amIn()) { addButton(langFactory(lng_profile_view_channel), SLOT(onViewChannel())); diff --git a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp index 86b138ad7..1e0e12fde 100644 --- a/Telegram/SourceFiles/profile/profile_fixed_bar.cpp +++ b/Telegram/SourceFiles/profile/profile_fixed_bar.cpp @@ -38,7 +38,7 @@ using UpdateFlag = Notify::PeerUpdate::Flag; const auto ButtonsUpdateFlags = UpdateFlag::UserCanShareContact | UpdateFlag::UserIsContact | UpdateFlag::ChatCanEdit - | UpdateFlag::ChannelAmEditor; + | UpdateFlag::ChannelRightsChanged; } // namespace @@ -117,13 +117,13 @@ void FixedBar::setChatActions() { } void FixedBar::setMegagroupActions() { - if (_peerMegagroup->amCreator() || _peerMegagroup->canEditInformation()) { + if (_peerMegagroup->canEditInformation()) { addRightAction(RightActionType::EditChannel, langFactory(lng_profile_edit_contact), SLOT(onEditChannel())); } } void FixedBar::setChannelActions() { - if (_peerChannel->amCreator()) { + if (_peerChannel->canEditInformation()) { addRightAction(RightActionType::EditChannel, langFactory(lng_profile_edit_contact), SLOT(onEditChannel())); } } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 702ccddf2..fe65b30ee 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -647,10 +647,10 @@ void ChatData::setName(const QString &newName) { void ChatData::invalidateParticipants() { auto wasCanEdit = canEdit(); - participants = ChatData::Participants(); - admins = ChatData::Admins(); + participants.clear(); + admins.clear(); flags &= ~MTPDchat::Flag::f_admin; - invitedByMe = ChatData::InvitedByMe(); + invitedByMe.clear(); botStatus = 0; if (wasCanEdit != canEdit()) { Notify::peerUpdatedDelayed(this, Notify::PeerUpdate::Flag::ChatCanEdit); @@ -748,6 +748,13 @@ void ChannelData::setAdminsCount(int newAdminsCount) { } } +void ChannelData::setRestrictedCount(int newRestrictedCount) { + if (_restrictedCount != newRestrictedCount) { + _restrictedCount = newRestrictedCount; + Notify::peerUpdatedDelayed(this, Notify::PeerUpdate::Flag::BlockedUsersChanged); + } +} + void ChannelData::setKickedCount(int newKickedCount) { if (_kickedCount != newKickedCount) { _kickedCount = newKickedCount; @@ -755,29 +762,187 @@ void ChannelData::setKickedCount(int newKickedCount) { } } +MTPChannelBannedRights ChannelData::KickedRestrictedRights() { + using Flag = MTPDchannelBannedRights::Flag; + auto flags = Flag::f_view_messages | Flag::f_send_messages | Flag::f_send_media | Flag::f_embed_links | Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline; + return MTP_channelBannedRights(MTP_flags(flags), MTP_int(std::numeric_limits::max())); +} + +void ChannelData::applyEditAdmin(gsl::not_null user, const MTPChannelAdminRights &rights) { + auto flags = Notify::PeerUpdate::Flag::AdminsChanged | Notify::PeerUpdate::Flag::None; + if (mgInfo) { + if (!mgInfo->lastParticipants.contains(user)) { // If rights are empty - still add participant? TODO check + mgInfo->lastParticipants.push_front(user); + setMembersCount(membersCount() + 1); + if (user->botInfo && !mgInfo->bots.contains(user)) { + mgInfo->bots.insert(user); + if (mgInfo->botStatus != 0 && mgInfo->botStatus < 2) { + mgInfo->botStatus = 2; + } + } + } + if (mgInfo->lastRestricted.contains(user)) { // If rights are empty - still remove restrictions? TODO check + mgInfo->lastRestricted.remove(user); + if (restrictedCount() > 0) { + setRestrictedCount(restrictedCount() - 1); + } + } + auto it = mgInfo->lastAdmins.find(user); + if (rights.c_channelAdminRights().vflags.v != 0) { + auto lastAdmin = MegagroupInfo::Admin { rights }; + lastAdmin.canEdit = true; + if (it == mgInfo->lastAdmins.cend()) { + mgInfo->lastAdmins.insert(user, lastAdmin); + setAdminsCount(adminsCount() + 1); + } else { + it.value() = lastAdmin; + } + } else { + if (it != mgInfo->lastAdmins.cend()) { + mgInfo->lastAdmins.erase(it); + if (adminsCount() > 0) { + setAdminsCount(adminsCount() - 1); + } + } + } + } + Notify::peerUpdatedDelayed(this, flags); +} + +void ChannelData::applyEditBanned(gsl::not_null user, const MTPChannelBannedRights &rights) { + auto flags = Notify::PeerUpdate::Flag::BlockedUsersChanged | Notify::PeerUpdate::Flag::None; + if (mgInfo) { + if (mgInfo->lastAdmins.contains(user)) { // If rights are empty - still remove admin? TODO check + mgInfo->lastAdmins.remove(user); + if (adminsCount() > 1) { + setAdminsCount(adminsCount() - 1); + } else { + flags |= Notify::PeerUpdate::Flag::AdminsChanged; + } + } + auto isKicked = (rights.c_channelBannedRights().vflags.v & MTPDchannelBannedRights::Flag::f_view_messages); + auto isRestricted = !isKicked && (rights.c_channelBannedRights().vflags.v != 0); + auto it = mgInfo->lastRestricted.find(user); + if (isRestricted) { + if (it == mgInfo->lastRestricted.cend()) { + mgInfo->lastRestricted.insert(user, MegagroupInfo::Restricted { rights }); + setRestrictedCount(restrictedCount() + 1); + } else { + it->rights = rights; + } + } else { + if (it != mgInfo->lastRestricted.cend()) { + mgInfo->lastRestricted.erase(it); + if (restrictedCount() > 0) { + setRestrictedCount(restrictedCount() - 1); + } + } + if (isKicked) { + auto i = mgInfo->lastParticipants.indexOf(user); + if (i >= 0) { + mgInfo->lastParticipants.removeAt(i); + } + if (membersCount() > 1) { + setMembersCount(membersCount() - 1); + } else { + mgInfo->lastParticipantsStatus |= MegagroupInfo::LastParticipantsCountOutdated; + mgInfo->lastParticipantsCount = 0; + } + setKickedCount(kickedCount() + 1); + if (mgInfo->bots.contains(user)) { + mgInfo->bots.remove(user); + if (mgInfo->bots.isEmpty() && mgInfo->botStatus > 0) { + mgInfo->botStatus = -1; + } + } + flags |= Notify::PeerUpdate::Flag::MembersChanged; + } + } + } + Notify::peerUpdatedDelayed(this, flags); +} + void ChannelData::flagsUpdated() { if (isMegagroup()) { if (!mgInfo) { - mgInfo = new MegagroupInfo(); + mgInfo = std::make_unique(); } } else if (mgInfo) { - delete mgInfo; mgInfo = nullptr; } } -void ChannelData::selfAdminUpdated() { +bool ChannelData::canNotEditLastAdmin(gsl::not_null user) const { + if (mgInfo) { + auto i = mgInfo->lastAdmins.constFind(user); + if (i != mgInfo->lastAdmins.cend()) { + return !i->canEdit; + } + return (user == mgInfo->creator); + } + return false; +} + +bool ChannelData::canEditAdmin(gsl::not_null user) const { + if (user->isSelf()) { + return false; + } else if (amCreator()) { + return true; + } else if (canNotEditLastAdmin(user)) { + return false; + } + return adminRights().is_add_admins(); +} + +bool ChannelData::canRestrictUser(gsl::not_null user) const { + if (user->isSelf()) { + return false; + } else if (amCreator()) { + return true; + } else if (canNotEditLastAdmin(user)) { + return false; + } + return adminRights().is_ban_users(); +} + +void ChannelData::setAdminRights(const MTPChannelAdminRights &rights) { + if (rights.c_channelAdminRights().vflags.v == _adminRights.c_channelAdminRights().vflags.v) { + return; + } + _adminRights = rights; if (isMegagroup()) { if (hasAdminRights()) { - mgInfo->lastAdmins.insert(App::self(), _adminRights); + if (!amCreator()) { + auto me = MegagroupInfo::Admin { _adminRights }; + me.canEdit = false; + mgInfo->lastAdmins.insert(App::self(), me); + } + mgInfo->lastRestricted.remove(App::self()); } else { mgInfo->lastAdmins.remove(App::self()); } } + Notify::peerUpdatedDelayed(this, UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged | UpdateFlag::BlockedUsersChanged); } -ChannelData::~ChannelData() { - delete mgInfo; +void ChannelData::setRestrictedRights(const MTPChannelBannedRights &rights) { + if (rights.c_channelBannedRights().vflags.v == _restrictedRights.c_channelBannedRights().vflags.v + && rights.c_channelBannedRights().vuntil_date.v == _restrictedRights.c_channelBannedRights().vuntil_date.v) { + return; + } + _restrictedRights = rights; + if (isMegagroup()) { + if (hasRestrictedRights()) { + if (!amCreator()) { + auto me = MegagroupInfo::Restricted { _restrictedRights }; + mgInfo->lastRestricted.insert(App::self(), me); + } + mgInfo->lastAdmins.remove(App::self()); + } else { + mgInfo->lastRestricted.remove(App::self()); + } + } + Notify::peerUpdatedDelayed(this, UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged | UpdateFlag::BlockedUsersChanged); } uint64 PtsWaiter::ptsKey(PtsSkippedQueue queue) { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index a48b579e7..f682d52c3 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -619,16 +619,11 @@ public: bool isMigrated() const { return flags & MTPDchat::Flag::f_migrated_to; } - typedef QMap Participants; - Participants participants; - typedef OrderedSet InvitedByMe; - InvitedByMe invitedByMe; - typedef OrderedSet Admins; - Admins admins; - typedef QList LastAuthors; - LastAuthors lastAuthors; - typedef OrderedSet MarkupSenders; - MarkupSenders markupSenders; + QMap, int> participants; + OrderedSet> invitedByMe; + OrderedSet> admins; + QList> lastAuthors; + OrderedSet> markupSenders; int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other // ImagePtr photoFull; @@ -705,14 +700,26 @@ private: }; struct MegagroupInfo { - typedef QList LastParticipants; - LastParticipants lastParticipants; - QMap lastAdmins; - typedef OrderedSet MarkupSenders; - MarkupSenders markupSenders; - typedef OrderedSet Bots; - Bots bots; + struct Admin { + explicit Admin(MTPChannelAdminRights rights) : rights(rights) { + } + Admin(MTPChannelAdminRights rights, bool canEdit) : rights(rights), canEdit(canEdit) { + } + MTPChannelAdminRights rights; + bool canEdit = false; + }; + struct Restricted { + explicit Restricted(MTPChannelBannedRights rights) : rights(rights) { + } + MTPChannelBannedRights rights; + }; + QList> lastParticipants; + QMap, Admin> lastAdmins; + QMap, Restricted> lastRestricted; + OrderedSet> markupSenders; + OrderedSet> bots; + UserData *creator = nullptr; // nullptr means unknown int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other MsgId pinnedMsgId = 0; bool joinedMessageFound = false; @@ -764,6 +771,11 @@ public: } void setAdminsCount(int newAdminsCount); + int restrictedCount() const { + return _restrictedCount; + } + void setRestrictedCount(int newRestrictedCount); + int kickedCount() const { return _kickedCount; } @@ -788,11 +800,15 @@ public: return flags & MTPDchannel::Flag::f_verified; } + static MTPChannelBannedRights KickedRestrictedRights(); + void applyEditAdmin(gsl::not_null user, const MTPChannelAdminRights &rights); + void applyEditBanned(gsl::not_null user, const MTPChannelBannedRights &rights); + int32 date = 0; int version = 0; MTPDchannel::Flags flags = { 0 }; MTPDchannelFull::Flags flagsFull = { 0 }; - MegagroupInfo *mgInfo = nullptr; + std::unique_ptr mgInfo; bool lastParticipantsCountOutdated() const { if (!mgInfo || !(mgInfo->lastParticipantsStatus & MegagroupInfo::LastParticipantsCountOutdated)) { return false; @@ -804,7 +820,6 @@ public: return true; } void flagsUpdated(); - void selfAdminUpdated(); bool isMegagroup() const { return flags & MTPDchannel::Flag::f_megagroup; } @@ -817,39 +832,44 @@ public: bool amCreator() const { return flags & MTPDchannel::Flag::f_creator; } - bool amEditor() const { - return hasAdminRights(); + const MTPChannelAdminRights &adminRightsBoxed() const { + return _adminRights; } const MTPDchannelAdminRights &adminRights() const { return _adminRights.c_channelAdminRights(); } - void setAdminRights(const MTPChannelAdminRights &rights) { - _adminRights = rights; - } + void setAdminRights(const MTPChannelAdminRights &rights); bool hasAdminRights() const { return (adminRights().vflags.v != 0); } - const MTPDchannelBannedRights &bannedRights() const { - return _bannedRights.c_channelBannedRights(); + const MTPChannelBannedRights &restrictedRightsBoxed() const { + return _restrictedRights; } - void setBannedRights(const MTPChannelBannedRights &rights) { - _bannedRights = rights; + const MTPDchannelBannedRights &restrictedRights() const { + return _restrictedRights.c_channelBannedRights(); } - bool hasBannedRights() const { - return (bannedRights().vflags.v != 0); + void setRestrictedRights(const MTPChannelBannedRights &rights); + bool hasRestrictedRights() const { + return (restrictedRights().vflags.v != 0); } - bool hasBannedRights(int32 now) const { - return hasBannedRights() && (bannedRights().vuntil_date.v > now); + bool hasRestrictedRights(int32 now) const { + return hasRestrictedRights() && (restrictedRights().vuntil_date.v > now); } bool canBanMembers() const { return adminRights().is_ban_users() || amCreator(); } + bool canEditMessages() const { + return adminRights().is_edit_messages() || amCreator(); + } bool canDeleteMessages() const { return adminRights().is_delete_messages() || amCreator(); } bool canAddMembers() const { return adminRights().is_invite_users() || amCreator() || (amIn() && (flags & MTPDchannel::Flag::f_democracy)); } + bool canAddAdmins() const { + return adminRights().is_add_admins() || amCreator(); + } bool canPinMessages() const { return adminRights().is_pin_messages() || amCreator(); } @@ -857,7 +877,7 @@ public: return adminRights().is_post_messages() || amCreator(); } bool canWrite() const { - return amIn() && (canPublish() || !isBroadcast()); + return amIn() && (canPublish() || (!isBroadcast() && !restrictedRights().is_send_messages())); } bool canViewMembers() const { return flagsFull & MTPDchannelFull::Flag::f_can_view_participants; @@ -875,11 +895,16 @@ public: constexpr auto kDeleteChannelMembersLimit = 1000; return amCreator() && (membersCount() <= kDeleteChannelMembersLimit); } + bool canEditAdmin(gsl::not_null user) const; + bool canRestrictUser(gsl::not_null user) const; void setInviteLink(const QString &newInviteLink); QString inviteLink() const { return _inviteLink; } + bool canHaveInviteLink() const { + return adminRights().is_invite_link() || amCreator(); + } int32 inviter = 0; // > 0 - user who invited me to channel, < 0 - not in channel QDateTime inviteDate; @@ -930,19 +955,20 @@ public: _restrictionReason = reason; } - ~ChannelData(); - private: + bool canNotEditLastAdmin(gsl::not_null user) const; + PtsWaiter _ptsWaiter; TimeMs _lastFullUpdate = 0; bool _isForbidden = true; int _membersCount = 1; int _adminsCount = 1; + int _restrictedCount = 0; int _kickedCount = 0; MTPChannelAdminRights _adminRights = MTP_channelAdminRights(MTP_flags(0)); - MTPChannelBannedRights _bannedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); + MTPChannelBannedRights _restrictedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); QString _restrictionReason; QString _about;