Allow restricting forwards in groups / channels.

This commit is contained in:
John Preston 2021-11-05 18:03:17 +04:00
parent 431e3035af
commit 9be47f0870
11 changed files with 105 additions and 13 deletions

View File

@ -148,6 +148,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_error_admin_limit" = "Sorry, you've reached the maximum number of admins for this group.";
"lng_error_admin_limit_channel" = "Sorry, you've reached the maximum number of admins for this channel.";
"lng_error_post_link_invalid" = "Unfortunately, you can't access this message. You are not a member of the chat where it was posted.";
"lng_error_noforwards_group" = "Sorry, forwarding is disabled from this group.";
"lng_error_noforwards_channel" = "Sorry, forwarding is disabled from this channel.";
"lng_sure_add_admin_invite" = "This user is not a member of this group. Add them to the group and promote them to admin?";
"lng_sure_add_admin_invite_channel" = "This user is not a subscriber of this channel. Add them to the channel and promote them to admin?";
"lng_sure_add_admin_unremove" = "This user is currently restricted or removed. Are you sure you want to promote them?";
@ -1808,6 +1810,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_edit_contact_title" = "Edit contact name";
"lng_edit_channel_title" = "Edit channel";
"lng_edit_sign_messages" = "Sign messages";
"lng_edit_allow_forwards" = "Allow saving content";
"lng_edit_group" = "Edit group";
"lng_edit_self_title" = "Edit your name";
"lng_confirm_contact_data" = "New Contact";

View File

@ -504,6 +504,11 @@ void ApiWrap::sendMessageFail(
scheduled.removeSending(item);
Ui::show(Box<Ui::InformBox>(tr::lng_cant_do_this(tr::now)));
}
} else if (error.type() == qstr("CHAT_FORWARDS_RESTRICTED")) {
Ui::ShowMultilineToast({ .text = { peer->isBroadcast()
? tr::lng_error_noforwards_channel(tr::now)
: tr::lng_error_noforwards_group(tr::now)
}, .duration = kJoinErrorDuration });
}
if (const auto item = _session->data().message(itemId)) {
Assert(randomId != 0);

View File

@ -277,6 +277,7 @@ private:
std::optional<QString> description;
std::optional<bool> hiddenPreHistory;
std::optional<bool> signatures;
std::optional<bool> forwards;
std::optional<ChannelData*> linkedChat;
};
@ -296,6 +297,7 @@ private:
void fillLinkedChatButton();
//void fillInviteLinkButton();
void fillSignaturesButton();
void fillForwardsButton();
void fillHistoryVisibilityButton();
void fillManageSection();
void fillPendingRequestsButton();
@ -312,6 +314,7 @@ private:
bool validateDescription(Saving &to) const;
bool validateHistoryVisibility(Saving &to) const;
bool validateSignatures(Saving &to) const;
bool validateForwards(Saving &to) const;
void save();
void saveUsername();
@ -320,6 +323,7 @@ private:
void saveDescription();
void saveHistoryVisibility();
void saveSignatures();
void saveForwards();
void savePhoto();
void pushSaveStage(FnMut<void()> &&lambda);
void continueSave();
@ -341,6 +345,7 @@ private:
std::optional<HistoryVisibility> _historyVisibilitySavedValue;
std::optional<QString> _usernameSavedValue;
std::optional<bool> _signaturesSavedValue;
std::optional<bool> _forwardsSavedValue;
const not_null<Window::SessionNavigation*> _navigation;
const not_null<Ui::BoxContent*> _box;
@ -795,6 +800,21 @@ void Controller::fillSignaturesButton() {
}, _controls.buttonsLayout->lifetime());
}
void Controller::fillForwardsButton() {
Expects(_controls.buttonsLayout != nullptr);
AddButtonWithText(
_controls.buttonsLayout,
tr::lng_edit_allow_forwards(),
rpl::single(QString()),
[=] {}
)->toggleOn(rpl::single(_peer->allowsForwarding())
)->toggledValue(
) | rpl::start_with_next([=](bool toggled) {
_forwardsSavedValue = toggled;
}, _controls.buttonsLayout->lifetime());
}
void Controller::fillHistoryVisibilityButton() {
Expects(_controls.buttonsLayout != nullptr);
@ -864,6 +884,9 @@ void Controller::fillManageSection() {
? (channel->canEditSignatures() && !channel->isMegagroup())
: false;
}();
const auto canEditForwards = [&] {
return isChannel ? channel->amCreator() : chat->amCreator();
}();
const auto canEditPreHistoryHidden = [&] {
return isChannel
? channel->canEditPreHistoryHidden()
@ -937,8 +960,12 @@ void Controller::fillManageSection() {
if (canEditSignatures) {
fillSignaturesButton();
}
if (canEditForwards) {
fillForwardsButton();
}
if (canEditPreHistoryHidden
|| canEditSignatures
|| canEditForwards
//|| canEditInviteLinks
|| canViewOrEditLinkedChat
|| canEditUsername) {
@ -1154,7 +1181,8 @@ std::optional<Controller::Saving> Controller::validate() const {
&& validateTitle(result)
&& validateDescription(result)
&& validateHistoryVisibility(result)
&& validateSignatures(result)) {
&& validateSignatures(result)
&& validateForwards(result)) {
return result;
}
return {};
@ -1229,6 +1257,14 @@ bool Controller::validateSignatures(Saving &to) const {
return true;
}
bool Controller::validateForwards(Saving &to) const {
if (!_forwardsSavedValue.has_value()) {
return true;
}
to.forwards = _forwardsSavedValue;
return true;
}
void Controller::save() {
Expects(_wrap != nullptr);
@ -1243,6 +1279,7 @@ void Controller::save() {
pushSaveStage([=] { saveDescription(); });
pushSaveStage([=] { saveHistoryVisibility(); });
pushSaveStage([=] { saveSignatures(); });
pushSaveStage([=] { saveForwards(); });
pushSaveStage([=] { savePhoto(); });
continueSave();
}
@ -1499,6 +1536,26 @@ void Controller::saveSignatures() {
}).send();
}
void Controller::saveForwards() {
if (!_savingData.forwards
|| *_savingData.forwards == _peer->allowsForwarding()) {
return continueSave();
}
_api.request(MTPmessages_ToggleNoForwards(
_peer->input,
MTP_bool(!*_savingData.forwards)
)).done([=](const MTPUpdates &result) {
_peer->session().api().applyUpdates(result);
continueSave();
}).fail([=](const MTP::Error &error) {
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
continueSave();
} else {
cancelSave();
}
}).send();
}
void Controller::savePhoto() {
auto image = _controls.photo
? _controls.photo->takeResultImage()

View File

@ -470,6 +470,10 @@ bool ChannelData::canWrite() const {
&& !amRestricted(Restriction::SendMessages)));
}
bool ChannelData::allowsForwarding() const {
return !(flags() & Flag::NoForwards);
}
bool ChannelData::canViewMembers() const {
return flags() & Flag::CanViewParticipants;
}

View File

@ -50,6 +50,7 @@ enum class ChannelDataFlag {
CanViewParticipants = (1 << 17),
HasLink = (1 << 18),
SlowmodeEnabled = (1 << 19),
NoForwards = (1 << 20),
};
inline constexpr bool is_flag_type(ChannelDataFlag) { return true; };
using ChannelDataFlags = base::flags<ChannelDataFlag>;
@ -293,6 +294,7 @@ public:
// Like in ChatData.
[[nodiscard]] bool canWrite() const;
[[nodiscard]] bool allowsForwarding() const;
[[nodiscard]] bool canEditInformation() const;
[[nodiscard]] bool canEditPermissions() const;
[[nodiscard]] bool canEditUsername() const;

View File

@ -28,7 +28,7 @@ ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
, inputChat(MTP_long(peerToChat(id).bare)) {
_flags.changes(
) | rpl::start_with_next([=](const Flags::Change &change) {
if (change.diff & ChatDataFlag::CallNotEmpty) {
if (change.diff & Flag::CallNotEmpty) {
if (const auto history = this->owner().historyLoaded(this)) {
history->updateChatListEntry();
}
@ -63,6 +63,10 @@ bool ChatData::canWrite() const {
return amIn() && !amRestricted(Restriction::SendMessages);
}
bool ChatData::allowsForwarding() const {
return !(flags() & Flag::NoForwards);
}
bool ChatData::canEditInformation() const {
return amIn() && !amRestricted(Restriction::ChangeInfo);
}
@ -74,7 +78,7 @@ bool ChatData::canEditPermissions() const {
bool ChatData::canEditUsername() const {
return amCreator()
&& (flags() & ChatDataFlag::CanSetUsername);
&& (flags() & Flag::CanSetUsername);
}
bool ChatData::canEditPreHistoryHidden() const {
@ -222,7 +226,7 @@ void ChatData::setGroupCall(
scheduleDate);
owner().registerGroupCall(_call.get());
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
addFlags(ChatDataFlag::CallActive);
addFlags(Flag::CallActive);
});
}
@ -236,7 +240,7 @@ void ChatData::clearGroupCall() {
_call = nullptr;
}
session().changes().peerUpdated(this, UpdateFlag::GroupCall);
removeFlags(ChatDataFlag::CallActive | ChatDataFlag::CallNotEmpty);
removeFlags(Flag::CallActive | Flag::CallNotEmpty);
}
void ChatData::setGroupCallDefaultJoinAs(PeerId peerId) {

View File

@ -18,6 +18,7 @@ enum class ChatDataFlag {
CallActive = (1 << 5),
CallNotEmpty = (1 << 6),
CanSetUsername = (1 << 7),
NoForwards = (1 << 8),
};
inline constexpr bool is_flag_type(ChatDataFlag) { return true; };
using ChatDataFlags = base::flags<ChatDataFlag>;
@ -109,6 +110,7 @@ public:
// Like in ChannelData.
[[nodiscard]] bool canWrite() const;
[[nodiscard]] bool allowsForwarding() const;
[[nodiscard]] bool canEditInformation() const;
[[nodiscard]] bool canEditPermissions() const;
[[nodiscard]] bool canEditUsername() const;

View File

@ -848,6 +848,17 @@ bool PeerData::canWrite() const {
return false;
}
bool PeerData::allowsForwarding() const {
if (const auto user = asUser()) {
return true;
} else if (const auto channel = asChannel()) {
return channel->allowsForwarding();
} else if (const auto chat = asChat()) {
return chat->allowsForwarding();
}
return false;
}
Data::RestrictionCheckResult PeerData::amRestricted(
ChatRestriction right) const {
using Result = Data::RestrictionCheckResult;

View File

@ -274,6 +274,7 @@ public:
}
[[nodiscard]] bool canWrite() const;
[[nodiscard]] bool allowsForwarding() const;
[[nodiscard]] Data::RestrictionCheckResult amRestricted(
ChatRestriction right) const;
[[nodiscard]] bool amAnonymous() const;

View File

@ -641,7 +641,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| Flag::Deactivated
| Flag::Forbidden
| Flag::CallActive
| Flag::CallNotEmpty;
| Flag::CallNotEmpty
| Flag::NoForwards;
const auto flagsSet = (data.is_left() ? Flag::Left : Flag())
| (data.is_kicked() ? Flag::Kicked : Flag())
| (data.is_creator() ? Flag::Creator : Flag())
@ -651,7 +652,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
|| (chat->groupCall()
&& chat->groupCall()->fullCount() > 0))
? Flag::CallNotEmpty
: Flag());
: Flag())
| (data.is_noforwards() ? Flag::NoForwards : Flag());
chat->setFlags((chat->flags() & ~flagsMask) | flagsSet);
chat->count = data.vparticipants_count().v;
@ -739,10 +741,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| Flag::CallActive
| Flag::CallNotEmpty
| Flag::Forbidden
| (!minimal
? Flag::Left
| Flag::Creator
: Flag());
| (!minimal ? (Flag::Left | Flag::Creator) : Flag())
| Flag::NoForwards;
const auto flagsSet = (data.is_broadcast() ? Flag::Broadcast : Flag())
| (data.is_verified() ? Flag::Verified : Flag())
| (data.is_scam() ? Flag::Scam : Flag())
@ -762,7 +762,8 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| (!minimal
? (data.is_left() ? Flag::Left : Flag())
| (data.is_creator() ? Flag::Creator : Flag())
: Flag());
: Flag())
| (data.is_noforwards() ? Flag::NoForwards : Flag());
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
channel->setName(

View File

@ -1038,7 +1038,9 @@ void HistoryMessage::applySentMessage(
}
bool HistoryMessage::allowsForward() const {
return isRegular() && (!_media || _media->allowsForward());
return isRegular()
&& history()->peer->allowsForwarding()
&& (!_media || _media->allowsForward());
}
bool HistoryMessage::allowsSendNow() const {