mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Allow editing my rank / other ranks.
This commit is contained in:
@@ -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 ¤tRank,
|
||||
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 ¤tRank,
|
||||
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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user