Allow editing my rank / other ranks.

This commit is contained in:
John Preston
2026-02-10 17:38:11 +04:00
parent 7969795a01
commit aa94491ad7
17 changed files with 437 additions and 116 deletions
+6 -3
View File
@@ -5981,9 +5981,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_rights_edit_admin_header" = "What can this admin do?";
"lng_rights_edit_admin_rank_name" = "Custom title";
"lng_rights_edit_admin_rank_about" = "A title that members will see instead of '{title}'.";
"lng_manage_peer_custom_ranks_title" = "Custom Member Titles";
"lng_manage_peer_custom_ranks" = "Allow Custom Titles";
"lng_manage_peer_custom_ranks_about" = "Allow members to set custom titles that will be shown next to their names in messages.";
"lng_manage_peer_custom_tags" = "Allow Members Tags";
"lng_manage_peer_custom_tags_about" = "Turn this on to let members of the group add short tags next to their names.";
"lng_context_add_my_tag" = "Add Tag";
"lng_context_edit_my_tag" = "Edit Tag";
"lng_context_add_member_tag" = "Add Member Tag";
"lng_context_edit_member_tag" = "Edit Member Tag";
"lng_rights_about_add_admins_yes" = "This admin will be able to add new admins with equal or fewer rights.";
"lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
"lng_rights_about_by" = "This admin promoted by {user} on {date}.";
@@ -34,44 +34,80 @@ namespace {
[[nodiscard]] object_ptr<Ui::RpWidget> CreateMembersVisibleButton(
not_null<ChannelData*> megagroup) {
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
const auto container = result.data();
const auto min = EnableHideMembersMin(megagroup);
if (!megagroup->canBanMembers() || megagroup->membersCount() < min) {
const auto showHideMembers = megagroup->canBanMembers()
&& megagroup->membersCount() >= min;
const auto showCustomTags = megagroup->amCreator();
if (!showHideMembers && !showCustomTags) {
return { nullptr };
}
struct State {
rpl::event_stream<bool> toggled;
};
Ui::AddSkip(container);
const auto state = container->lifetime().make_state<State>();
const auto button = container->add(
EditPeerInfoBox::CreateButton(
container,
tr::lng_profile_hide_participants(),
rpl::single(QString()),
[] {},
st::manageGroupNoIconButton,
{}
))->toggleOn(rpl::single(
(megagroup->flags() & ChannelDataFlag::ParticipantsHidden) != 0
) | rpl::then(state->toggled.events()));
Ui::AddSkip(container);
Ui::AddDividerText(container, tr::lng_profile_hide_participants_about());
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
const auto container = result.data();
button->toggledValue(
) | rpl::on_next([=](bool toggled) {
megagroup->session().api().request(
MTPchannels_ToggleParticipantsHidden(
megagroup->inputChannel(),
MTP_bool(toggled)
)
).done([=](const MTPUpdates &result) {
megagroup->session().api().applyUpdates(result);
}).send();
}, button->lifetime());
if (showHideMembers) {
struct State {
rpl::event_stream<bool> toggled;
};
Ui::AddSkip(container);
const auto state = container->lifetime().make_state<State>();
const auto button = container->add(
EditPeerInfoBox::CreateButton(
container,
tr::lng_profile_hide_participants(),
rpl::single(QString()),
[] {},
st::manageGroupNoIconButton,
{}
))->toggleOn(rpl::single(
(megagroup->flags() & ChannelDataFlag::ParticipantsHidden) != 0
) | rpl::then(state->toggled.events()));
Ui::AddSkip(container);
Ui::AddDividerText(
container,
tr::lng_profile_hide_participants_about());
button->toggledValue(
) | rpl::on_next([=](bool toggled) {
megagroup->session().api().request(
MTPchannels_ToggleParticipantsHidden(
megagroup->inputChannel(),
MTP_bool(toggled)
)
).done([=](const MTPUpdates &result) {
megagroup->session().api().applyUpdates(result);
}).send();
}, button->lifetime());
}
if (showCustomTags) {
Ui::AddSkip(container);
const auto tagsButton = container->add(
EditPeerInfoBox::CreateButton(
container,
tr::lng_manage_peer_custom_tags(),
rpl::single(QString()),
[] {},
st::manageGroupNoIconButton,
{}
))->toggleOn(rpl::single(megagroup->customRanksEnabled()));
Ui::AddSkip(container);
Ui::AddDividerText(
container,
tr::lng_manage_peer_custom_tags_about());
tagsButton->toggledValue(
) | rpl::on_next([=](bool toggled) {
megagroup->session().api().request(
MTPmessages_ToggleChatCustomRanks(
megagroup->input(),
MTP_bool(toggled)
)
).done([=](const MTPUpdates &result) {
megagroup->session().api().applyUpdates(result);
}).send();
}, tagsButton->lifetime());
}
return result;
}
@@ -30,12 +30,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "base/unixtime.h"
#include "ui/effects/outline_segments.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/menu/menu_multiline_action.h"
#include "ui/widgets/popup_menu.h"
#include "ui/text/text_utilities.h"
#include "info/profile/info_profile_values.h"
#include "window/window_session_controller.h"
#include "history/history.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_menu_icons.h"
@@ -168,6 +171,48 @@ void SaveChannelAdmin(
}).send();
}
void SaveMemberRank(
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
not_null<UserData*> user,
const QString &rank,
Fn<void()> onDone,
Fn<void()> onFail) {
peer->session().api().request(MTPmessages_EditChatParticipantRank(
peer->input(),
user->input(),
MTP_string(rank)
)).done([=](const MTPUpdates &result) {
peer->session().api().applyUpdates(result);
if (const auto channel = peer->asChannel()) {
channel->applyEditMemberRank(user, rank);
} else if (const auto chat = peer->asChat()) {
if (rank.isEmpty()) {
chat->memberRanks.remove(peerToUser(user->id));
} else {
chat->memberRanks[peerToUser(user->id)] = rank;
}
chat->session().changes().peerUpdated(
chat,
Data::PeerUpdate::Flag::Members);
}
peer->session().changes().chatMemberRankChanged(
peer,
user,
rank);
if (onDone) {
onDone();
}
}).fail([=](const MTP::Error &error) {
if (show) {
show->showToast(error.type());
}
if (onFail) {
onFail();
}
}).send();
}
void SaveChatParticipantKick(
not_null<ChatData*> chat,
not_null<UserData*> user,
@@ -616,6 +661,16 @@ void ParticipantsAdditionalData::applyBannedLocally(
}
}
void ParticipantsAdditionalData::applyMemberRankLocally(
not_null<UserData*> user,
const QString &rank) {
if (rank.isEmpty()) {
_memberRanks.remove(user);
} else {
_memberRanks[user] = rank;
}
}
PeerData *ParticipantsAdditionalData::applyParticipant(
const Api::ChatParticipant &data) {
return applyParticipant(data, _role);
@@ -1316,6 +1371,16 @@ void ParticipantsBoxController::prepare() {
recomputeTypeFor(user);
refreshRows();
}, lifetime());
_peer->session().changes().chatMemberRankChanges(
) | rpl::on_next([=](const Data::ChatMemberRankChange &update) {
if (update.peer != _peer) {
return;
}
_additional.applyMemberRankLocally(update.user, update.rank);
recomputeTypeFor(update.user);
refreshRows();
}, lifetime());
}
void ParticipantsBoxController::unload() {
@@ -1754,6 +1819,57 @@ base::unique_qptr<Ui::PopupMenu> ParticipantsBoxController::rowContextMenu(
? &st::menuIconProfile
: &st::menuIconInfo));
}
if (user) {
const auto isAdmin = _peer->isChat()
? (_peer->asChat()->hasAdminRights()
|| _peer->asChat()->amCreator())
: (_peer->isChannel()
? (_peer->asChannel()->hasAdminRights()
|| _peer->asChannel()->amCreator())
: false);
const auto isSelf = user->isSelf();
const auto canEditSelf = isSelf
&& (isAdmin
|| (_peer->isChat()
? _peer->asChat()->customRanksEnabled()
: (_peer->isChannel()
? _peer->asChannel()->customRanksEnabled()
: false)));
const auto targetIsAdmin = _additional.adminRights(user).has_value()
|| _additional.isCreator(user);
const auto canEditTarget = !isSelf
&& isAdmin
&& (!targetIsAdmin || _additional.canEditAdmin(user));
if (canEditSelf || canEditTarget) {
const auto currentRank = _additional.memberRank(user);
const auto show = delegate()->peerListUiShow();
const auto peer = _peer;
const auto actionText = canEditSelf
? (currentRank.isEmpty()
? tr::lng_context_add_my_tag(tr::now)
: tr::lng_context_edit_my_tag(tr::now))
: (currentRank.isEmpty()
? tr::lng_context_add_member_tag(tr::now)
: tr::lng_context_edit_member_tag(tr::now));
result->addAction(
actionText,
crl::guard(this, [=] {
_editBox = show->show(Box(
EditCustomRankBox,
show,
peer,
user,
currentRank,
canEditSelf,
crl::guard(this, [=](const QString &rank) {
_additional.applyMemberRankLocally(user, rank);
recomputeTypeFor(user);
refreshRows();
})));
}),
&st::menuIconEdit);
}
}
if (_role == Role::Kicked) {
if (_peer->isMegagroup()
&& _additional.canRestrictParticipant(participant)) {
@@ -2464,3 +2580,71 @@ void ParticipantsBoxSearchController::searchDone(
delegate()->peerListSearchRefreshRows();
}
void EditCustomRankBox(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
not_null<UserData*> user,
const QString &currentRank,
bool isSelf,
Fn<void(QString rank)> onSaved) {
constexpr auto kRankLimit = 16;
struct State {
bool saving = false;
};
const auto state = box->lifetime().make_state<State>();
const auto hasRank = !currentRank.isEmpty();
box->setTitle(isSelf
? (hasRank
? tr::lng_context_edit_my_tag()
: tr::lng_context_add_my_tag())
: (hasRank
? tr::lng_context_edit_member_tag()
: tr::lng_context_add_member_tag()));
const auto field = box->addRow(object_ptr<Ui::InputField>(
box,
st::customBadgeField,
tr::lng_rights_edit_admin_rank_name(),
TextUtilities::RemoveEmoji(currentRank)));
field->setMaxLength(kRankLimit);
field->setInstantReplaces(Ui::InstantReplaces::TextOnly());
field->changes(
) | rpl::on_next([=] {
const auto text = field->getLastText();
const auto removed = TextUtilities::RemoveEmoji(text);
if (removed != text) {
field->setText(removed);
}
}, field->lifetime());
box->setFocusCallback([=] { field->setFocusFast(); });
const auto close = crl::guard(box, [=] { box->closeBox(); });
const auto save = [=] {
if (state->saving) {
return;
}
state->saving = true;
const auto rank = TextUtilities::RemoveEmoji(
TextUtilities::SingleLine(field->getLastText().trimmed()));
SaveMemberRank(
show,
peer,
user,
rank,
[=] {
if (onSaved) {
onSaved(rank);
}
close();
},
[=] { state->saving = false; });
};
field->submits(
) | rpl::on_next([=] { save(); }, field->lifetime());
box->addButton(tr::lng_settings_save(), save);
box->addButton(tr::lng_cancel(), close);
}
@@ -49,6 +49,15 @@ Fn<void(
Fn<void(ChatRestrictionsInfo newRights)> onDone,
Fn<void()> onFail);
void EditCustomRankBox(
not_null<Ui::GenericBox*> box,
std::shared_ptr<Ui::Show> show,
not_null<PeerData*> peer,
not_null<UserData*> user,
const QString &currentRank,
bool isSelf,
Fn<void(QString rank)> onSaved);
void SubscribeToMigration(
not_null<PeerData*> peer,
rpl::lifetime &lifetime,
@@ -130,6 +139,9 @@ public:
void applyBannedLocally(
not_null<PeerData*> participant,
ChatRestrictionsInfo rights);
void applyMemberRankLocally(
not_null<UserData*> user,
const QString &rank);
private:
UserData *applyCreator(const Api::ChatParticipant &data);
@@ -405,7 +405,6 @@ private:
std::optional<bool> signatures;
std::optional<bool> signatureProfiles;
std::optional<bool> noForwards;
std::optional<bool> customRanks;
std::optional<bool> joinToWrite;
std::optional<bool> requestToJoin;
std::optional<ChannelData*> discussionLink;
@@ -466,7 +465,6 @@ private:
[[nodiscard]] bool validateAutotranslate(Saving &to) const;
[[nodiscard]] bool validateSignatures(Saving &to) const;
[[nodiscard]] bool validateForwards(Saving &to) const;
[[nodiscard]] bool validateCustomRanks(Saving &to) const;
[[nodiscard]] bool validateJoinToWrite(Saving &to) const;
[[nodiscard]] bool validateRequestToJoin(Saving &to) const;
@@ -482,7 +480,6 @@ private:
void saveAutotranslate();
void saveSignatures();
void saveForwards();
void saveCustomRanks();
void saveJoinToWrite();
void saveRequestToJoin();
void savePhoto();
@@ -1002,11 +999,6 @@ void Controller::fillPrivacyTypeButton() {
? _peer->asChannel()->usernames()
: std::vector<QString>()),
.noForwards = !_peer->allowsForwarding(),
.customRanks = (_peer->isChat()
? _peer->asChat()->customRanksEnabled()
: _peer->isChannel()
? _peer->asChannel()->customRanksEnabled()
: false),
.joinToWrite = (_peer->isMegagroup()
&& _peer->asChannel()->joinToWrite()),
.requestToJoin = (_peer->isMegagroup()
@@ -2104,7 +2096,6 @@ std::optional<Controller::Saving> Controller::validate() const {
&& validateAutotranslate(result)
&& validateSignatures(result)
&& validateForwards(result)
&& validateCustomRanks(result)
&& validateJoinToWrite(result)
&& validateRequestToJoin(result)) {
return result;
@@ -2227,14 +2218,6 @@ bool Controller::validateForwards(Saving &to) const {
return true;
}
bool Controller::validateCustomRanks(Saving &to) const {
if (!_typeDataSavedValue) {
return true;
}
to.customRanks = _typeDataSavedValue->customRanks;
return true;
}
bool Controller::validateJoinToWrite(Saving &to) const {
if (!_typeDataSavedValue) {
return true;
@@ -2270,7 +2253,6 @@ void Controller::save() {
pushSaveStage([=] { saveAutotranslate(); });
pushSaveStage([=] { saveSignatures(); });
pushSaveStage([=] { saveForwards(); });
pushSaveStage([=] { saveCustomRanks(); });
pushSaveStage([=] { saveJoinToWrite(); });
pushSaveStage([=] { saveRequestToJoin(); });
pushSaveStage([=] { savePhoto(); });
@@ -2779,32 +2761,6 @@ void Controller::saveForwards() {
}).send();
}
void Controller::saveCustomRanks() {
const auto isEnabled = _peer->isChat()
? _peer->asChat()->customRanksEnabled()
: _peer->isChannel()
? _peer->asChannel()->customRanksEnabled()
: false;
if (!_savingData.customRanks
|| *_savingData.customRanks == isEnabled) {
return continueSave();
}
_api.request(MTPmessages_ToggleChatCustomRanks(
_peer->input(),
MTP_bool(*_savingData.customRanks)
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
continueSave();
}).fail([=](const MTP::Error &error) {
if (error.type() == u"CHAT_NOT_MODIFIED"_q) {
continueSave();
} else {
_navigation->showToast(error.type());
cancelSave();
}
}).send();
}
void Controller::saveJoinToWrite() {
const auto joinToWrite = _peer->isMegagroup()
&& _peer->asChannel()->joinToWrite();
@@ -83,10 +83,6 @@ public:
[[nodiscard]] bool noForwards() const {
return _controls.noForwards->toggled();
}
[[nodiscard]] bool customRanks() const {
return _controls.customRanks
&& _controls.customRanks->toggled();
}
[[nodiscard]] bool joinToWrite() const {
return _controls.joinToWrite && _controls.joinToWrite->toggled();
}
@@ -116,7 +112,6 @@ private:
Ui::SlideWrap<Ui::VerticalLayout> *whoSendWrap = nullptr;
Ui::SettingsButton *noForwards = nullptr;
Ui::SettingsButton *customRanks = nullptr;
Ui::SettingsButton *joinToWrite = nullptr;
Ui::SettingsButton *requestToJoin = nullptr;
};
@@ -302,34 +297,6 @@ void Controller::createContent() {
? tr::lng_manage_peer_no_forwards_about
: tr::lng_manage_peer_no_forwards_about_channel)());
const auto amCreator = (_peer->isChat()
&& _peer->asChat()->amCreator())
|| (_peer->isChannel()
&& _peer->asChannel()->amCreator());
if (amCreator) {
Ui::AddSkip(_wrap.get());
Ui::AddSubsectionTitle(
_wrap.get(),
tr::lng_manage_peer_custom_ranks_title());
_controls.customRanks = _wrap->add(
EditPeerInfoBox::CreateButton(
_wrap.get(),
tr::lng_manage_peer_custom_ranks(),
rpl::single(QString()),
[] {},
st::peerPermissionsButton,
{}));
_controls.customRanks->toggleOn(
rpl::single(_dataSavedValue->customRanks)
)->toggledValue(
) | rpl::on_next([=](bool toggled) {
_dataSavedValue->customRanks = toggled;
}, _wrap->lifetime());
Ui::AddSkip(_wrap.get());
Ui::AddDividerText(
_wrap.get(),
tr::lng_manage_peer_custom_ranks_about());
}
}
if (_linkOnly) {
_controls.inviteLinkWrap->show(anim::type::instant);
@@ -811,7 +778,6 @@ void EditPeerTypeBox::prepare() {
? controller->usernamesOrder()
: std::vector<QString>()),
.noForwards = controller->noForwards(),
.customRanks = controller->customRanks(),
.joinToWrite = controller->joinToWrite(),
.requestToJoin = controller->requestToJoin(),
}); // We don't need username with private type.
@@ -39,7 +39,6 @@ struct EditPeerTypeData {
std::vector<QString> usernamesOrder;
bool hasDiscussionLink = false;
bool noForwards = false;
bool customRanks = false;
bool joinToWrite = false;
bool requestToJoin = false;
};
@@ -357,6 +357,17 @@ rpl::producer<ChatAdminChange> Changes::chatAdminChanges() const {
return _chatAdminChanges.events();
}
void Changes::chatMemberRankChanged(
not_null<PeerData*> peer,
not_null<UserData*> user,
QString rank) {
_chatMemberRankChanges.fire({ peer, user, std::move(rank) });
}
rpl::producer<ChatMemberRankChange> Changes::chatMemberRankChanges() const {
return _chatMemberRankChanges.events();
}
void Changes::scheduleNotifications() {
if (!_notify) {
_notify = true;
+13
View File
@@ -282,6 +282,12 @@ struct ChatAdminChange {
QString rank;
};
struct ChatMemberRankChange {
not_null<PeerData*> peer;
not_null<UserData*> user;
QString rank;
};
class Changes final {
public:
explicit Changes(not_null<Main::Session*> session);
@@ -401,6 +407,12 @@ public:
QString rank);
[[nodiscard]] rpl::producer<ChatAdminChange> chatAdminChanges() const;
void chatMemberRankChanged(
not_null<PeerData*> peer,
not_null<UserData*> user,
QString rank);
[[nodiscard]] rpl::producer<ChatMemberRankChange> chatMemberRankChanges() const;
void sendNotifications();
private:
@@ -454,6 +466,7 @@ private:
Manager<Dialogs::Entry, EntryUpdate> _entryChanges;
Manager<Story, StoryUpdate> _storyChanges;
rpl::event_stream<ChatAdminChange> _chatAdminChanges;
rpl::event_stream<ChatMemberRankChange> _chatMemberRankChanges;
bool _notify = false;
@@ -510,6 +510,17 @@ void ChannelData::applyEditAdmin(
session().changes().peerUpdated(this, UpdateFlag::Admins);
}
void ChannelData::applyEditMemberRank(
not_null<UserData*> user,
const QString &rank) {
if (!mgInfo) {
return;
}
const auto userId = peerToUser(user->id);
Data::ChannelMemberRankChanges changes(this);
changes.feed(userId, rank);
}
void ChannelData::applyEditBanned(
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
+3
View File
@@ -308,6 +308,9 @@ public:
ChatAdminRightsInfo oldRights,
ChatAdminRightsInfo newRights,
const QString &rank);
void applyEditMemberRank(
not_null<UserData*> user,
const QString &rank);
void applyEditBanned(
not_null<PeerData*> participant,
ChatRestrictionsInfo oldRights,
@@ -44,4 +44,37 @@ ChannelAdminChanges::~ChannelAdminChanges() {
}
}
ChannelMemberRankChanges::ChannelMemberRankChanges(
not_null<ChannelData*> channel)
: _channel(channel)
, _memberRanks(_channel->mgInfo->memberRanks) {
}
void ChannelMemberRankChanges::feed(
UserId userId,
const QString &rank) {
if (rank.isEmpty()) {
if (_memberRanks.remove(userId)) {
_changes.emplace(userId);
}
} else {
const auto i = _memberRanks.find(userId);
if (i == end(_memberRanks) || i->second != rank) {
_memberRanks[userId] = rank;
_changes.emplace(userId);
}
}
}
ChannelMemberRankChanges::~ChannelMemberRankChanges() {
if (_changes.size() > 1
|| (!_changes.empty()
&& _changes.front() != _channel->session().userId())) {
if (const auto history
= _channel->owner().historyLoaded(_channel)) {
history->applyGroupAdminChanges(_changes);
}
}
}
} // namespace Data
@@ -25,4 +25,19 @@ private:
};
class ChannelMemberRankChanges {
public:
ChannelMemberRankChanges(not_null<ChannelData*> channel);
void feed(UserId userId, const QString &rank);
~ChannelMemberRankChanges();
private:
not_null<ChannelData*> _channel;
base::flat_map<UserId, QString> &_memberRanks;
base::flat_set<UserId> _changes;
};
} // namespace Data
@@ -8964,6 +8964,7 @@ void HistoryWidget::fillSenderUserpicMenu(
Window::FillSenderUserpicMenu(
controller(),
peer,
inGroup ? _peer : nullptr,
(inGroup && _canSendTexts) ? _field.data() : nullptr,
inGroup ? _peer->owner().history(_peer) : Dialogs::Key(),
Ui::Menu::CreateAddActionCallback(menu));
@@ -3323,9 +3323,15 @@ base::unique_qptr<Ui::PopupMenu> ChatWidget::listFillSenderUserpicMenu(
auto menu = base::make_unique_q<Ui::PopupMenu>(
this,
st::popupMenuWithIcons);
const auto senderPeer = _history->owner().peer(userpicPeerId);
const auto groupPeer = (_history->peer->isChat()
|| _history->peer->isMegagroup())
? _history->peer.get()
: nullptr;
Window::FillSenderUserpicMenu(
controller(),
_history->owner().peer(userpicPeerId),
senderPeer,
groupPeer,
_composeControls->fieldForMention(),
searchInEntry,
Ui::Menu::CreateAddActionCallback(menu.get()));
@@ -109,6 +109,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/ui_integration.h"
#include "export/export_manager.h"
#include "boxes/peers/edit_participants_box.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "boxes/premium_preview_box.h"
#include "styles/style_chat.h"
@@ -181,6 +182,24 @@ namespace {
constexpr auto kArchivedToastDuration = crl::time(5000);
constexpr auto kMaxUnreadWithoutConfirmation = 1000;
[[nodiscard]] QString LookupMemberRank(
not_null<PeerData*> peer,
not_null<UserData*> user) {
if (const auto chat = peer->asChat()) {
const auto i = chat->memberRanks.find(peerToUser(user->id));
return (i != chat->memberRanks.end()) ? i->second : QString();
} else if (const auto channel = peer->asChannel()) {
if (channel->mgInfo) {
const auto i = channel->mgInfo->memberRanks.find(
peerToUser(user->id));
return (i != channel->mgInfo->memberRanks.end())
? i->second
: QString();
}
}
return QString();
}
base::options::toggle ViewProfileInChatsListContextMenu({
.id = kOptionViewProfileInChatsListContextMenu,
.name = "Add \"View Profile\"",
@@ -3761,6 +3780,7 @@ bool FillVideoChatMenu(
void FillSenderUserpicMenu(
not_null<SessionController*> controller,
not_null<PeerData*> peer,
PeerData *groupPeer,
Ui::InputField *fieldForMention,
Dialogs::Key searchInEntry,
const PeerMenuCallback &addAction) {
@@ -3803,6 +3823,57 @@ void FillSenderUserpicMenu(
controller->searchInChat(searchInEntry, peer);
}, &st::menuIconSearch);
}
if (const auto user = peer->asUser()) {
if (groupPeer) {
const auto isAdmin = groupPeer->isChat()
? (groupPeer->asChat()->hasAdminRights()
|| groupPeer->asChat()->amCreator())
: (groupPeer->isChannel()
? (groupPeer->asChannel()->hasAdminRights()
|| groupPeer->asChannel()->amCreator())
: false);
const auto canEditTarget = [&] {
if (const auto chat = groupPeer->asChat()) {
if (peerToUser(user->id) == chat->creator) {
return chat->amCreator();
}
if (chat->admins.contains(user)) {
return chat->amCreator();
}
return true;
} else if (const auto channel = groupPeer->asChannel()) {
if (channel->mgInfo
&& (channel->mgInfo->lastAdmins.contains(user)
|| channel->mgInfo->creator == user)) {
return channel->canEditAdmin(user);
}
return true;
}
return false;
}();
if (isAdmin && canEditTarget && !user->isSelf()) {
const auto currentRank = LookupMemberRank(
groupPeer,
user);
addAction(
(currentRank.isEmpty()
? tr::lng_context_add_member_tag(tr::now)
: tr::lng_context_edit_member_tag(tr::now)),
[=] {
controller->show(Box(
EditCustomRankBox,
controller->uiShow(),
groupPeer,
user,
currentRank,
false,
nullptr));
},
&st::menuIconEdit);
}
}
}
}
void AddSenderUserpicModerateAction(
@@ -81,6 +81,7 @@ bool FillVideoChatMenu(
void FillSenderUserpicMenu(
not_null<SessionController*> controller,
not_null<PeerData*> peer,
PeerData *groupPeer,
Ui::InputField *fieldForMention,
Dialogs::Key searchInEntry,
const PeerMenuCallback &addAction);