Implement correct ForumTopic::canWrite logic.

This commit is contained in:
John Preston 2022-10-25 11:20:22 +04:00
parent 1cd02fc3c9
commit 602ba5bba9
35 changed files with 363 additions and 144 deletions

View file

@ -513,6 +513,12 @@ void ApiWrap::sendMessageFail(
Assert(randomId != 0); Assert(randomId != 0);
_session->data().unregisterMessageRandomId(randomId); _session->data().unregisterMessageRandomId(randomId);
item->sendFailed(); item->sendFailed();
if (error == u"TOPIC_CLOSED"_q) {
if (const auto topic = item->topic()) {
topic->setClosed(true);
}
}
} }
} }
@ -3005,7 +3011,10 @@ void ApiWrap::finishForwarding(const SendAction &action) {
if (!toForward.items.empty()) { if (!toForward.items.empty()) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
history->peer, history->peer,
toForward.items); {
.topicRootId = action.topicRootId,
.forward = &toForward.items,
});
if (!error.isEmpty()) { if (!error.isEmpty()) {
return; return;
} }
@ -3074,6 +3083,9 @@ void ApiWrap::forwardMessages(
if (sendAs) { if (sendAs) {
sendFlags |= MTPmessages_ForwardMessages::Flag::f_send_as; sendFlags |= MTPmessages_ForwardMessages::Flag::f_send_as;
} }
if (action.topicRootId) {
sendFlags |= MTPmessages_ForwardMessages::Flag::f_top_msg_id;
}
auto forwardFrom = draft.items.front()->history()->peer; auto forwardFrom = draft.items.front()->history()->peer;
auto ids = QVector<MTPint>(); auto ids = QVector<MTPint>();
@ -3093,7 +3105,7 @@ void ApiWrap::forwardMessages(
MTP_vector<MTPint>(ids), MTP_vector<MTPint>(ids),
MTP_vector<MTPlong>(randomIds), MTP_vector<MTPlong>(randomIds),
peer->input, peer->input,
MTPint(), // top_msg_id MTP_int(action.topicRootId),
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty())
)).done([=](const MTPUpdates &result) { )).done([=](const MTPUpdates &result) {
@ -3145,7 +3157,7 @@ void ApiWrap::forwardMessages(
HistoryItem::NewMessageDate(action.options.scheduled), HistoryItem::NewMessageDate(action.options.scheduled),
messageFromId, messageFromId,
messagePostAuthor, messagePostAuthor,
item); item); // #TODO forum forward
_session->data().registerMessageRandomId(randomId, newId); _session->data().registerMessageRandomId(randomId, newId);
if (!localIds) { if (!localIds) {
localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>(); localIds = std::make_shared<base::flat_map<uint64, FullMsgId>>();
@ -3405,7 +3417,13 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
action.generateLocal = true; action.generateLocal = true;
sendAction(action); sendAction(action);
if (!peer->canWrite() || Api::SendDice(message)) { const auto replyToId = message.action.replyTo;
const auto replyTo = replyToId
? peer->owner().message(peer, replyToId)
: nullptr;
const auto topic = replyTo ? replyTo->topic() : nullptr;
if (!(topic ? topic->canWrite() : peer->canWrite())
|| Api::SendDice(message)) {
return; return;
} }
local().saveRecentSentHashtags(textWithTags.text); local().saveRecentSentHashtags(textWithTags.text);

View file

@ -342,7 +342,7 @@ auto AddBotToGroupBoxController::createRow(not_null<History*> history)
bool AddBotToGroupBoxController::needToCreateRow( bool AddBotToGroupBoxController::needToCreateRow(
not_null<PeerData*> peer) const { not_null<PeerData*> peer) const {
if (sharingBotGame()) { if (sharingBotGame()) {
if (!peer->canWrite() if (!peer->canWrite() // #TODO forum forward
|| peer->amRestricted(ChatRestriction::SendGames)) { || peer->amRestricted(ChatRestriction::SendGames)) {
return false; return false;
} }

View file

@ -1153,8 +1153,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
for (const auto peer : result) { for (const auto peer : result) {
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, peer,
{}, { .text = &comment });
comment);
if (!error.isEmpty()) { if (!error.isEmpty()) {
return std::make_pair(error, peer); return std::make_pair(error, peer);
} }
@ -1205,7 +1204,7 @@ object_ptr<Ui::BoxContent> ShareInviteLinkBox(
auto object = Box<ShareBox>(ShareBox::Descriptor{ auto object = Box<ShareBox>(ShareBox::Descriptor{
.session = &peer->session(), .session = &peer->session(),
.copyCallback = std::move(copyCallback), .copyCallback = std::move(copyCallback),
.submitCallback = std::move(submitCallback), .submitCallback = std::move(submitCallback), // #TODO forum forward
.filterCallback = [](auto peer) { return peer->canWrite(); }, .filterCallback = [](auto peer) { return peer->canWrite(); },
}); });
*box = Ui::MakeWeak(object.data()); *box = Ui::MakeWeak(object.data());

View file

@ -1292,11 +1292,10 @@ void FastShareMessage(
} }
const auto error = [&] { const auto error = [&] {
for (const auto peer : result) { for (const auto peer : result) { // #TODO forum forward
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, peer,
items, { .forward = &items, .text = &comment });
comment);
if (!error.isEmpty()) { if (!error.isEmpty()) {
return std::make_pair(error, peer); return std::make_pair(error, peer);
} }
@ -1399,7 +1398,7 @@ void FastShareMessage(
} }
}; };
auto filterCallback = [isGame](PeerData *peer) { auto filterCallback = [isGame](PeerData *peer) {
if (peer->canWrite()) { if (peer->canWrite()) { // #TODO forum forward
if (auto channel = peer->asChannel()) { if (auto channel = peer->asChannel()) {
return isGame ? (!channel->isBroadcast()) : true; return isGame ? (!channel->isBroadcast()) : true;
} }

View file

@ -1642,7 +1642,7 @@ void Members::setupAddMember(not_null<GroupCall*> call) {
return rpl::single(false) | rpl::type_erased(); return rpl::single(false) | rpl::type_erased();
} }
return rpl::combine( return rpl::combine(
Data::CanWriteValue(peer.get()), Data::CanWriteValue(peer, false),
_call->joinAsValue() _call->joinAsValue()
) | rpl::map([=](bool can, not_null<PeerData*> joinAs) { ) | rpl::map([=](bool can, not_null<PeerData*> joinAs) {
return can && joinAs->isSelf(); return can && joinAs->isSelf();

View file

@ -845,7 +845,7 @@ void Panel::setupMembers() {
_members->addMembersRequests( _members->addMembersRequests(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
if (!_peer->isBroadcast() if (!_peer->isBroadcast()
&& _peer->canWrite() && _peer->canWrite(false)
&& _call->joinAs()->isSelf()) { && _call->joinAs()->isSelf()) {
addMembers(); addMembers();
} else if (const auto channel = _peer->asChannel()) { } else if (const auto channel = _peer->asChannel()) {

View file

@ -141,11 +141,10 @@ object_ptr<ShareBox> ShareInviteLinkBox(
} }
const auto error = [&] { const auto error = [&] {
for (const auto peer : result) { for (const auto peer : result) { // #TODO forum forward
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, peer,
{}, { .text = &comment });
comment);
if (!error.isEmpty()) { if (!error.isEmpty()) {
return std::make_pair(error, peer); return std::make_pair(error, peer);
} }
@ -196,7 +195,7 @@ object_ptr<ShareBox> ShareInviteLinkBox(
showToast(tr::lng_share_done(tr::now)); showToast(tr::lng_share_done(tr::now));
}; };
auto filterCallback = [](PeerData *peer) { auto filterCallback = [](PeerData *peer) {
return peer->canWrite(); return peer->canWrite(); // #TODO forum forward
}; };
const auto scheduleStyle = [&] { const auto scheduleStyle = [&] {

View file

@ -552,11 +552,14 @@ bool ChannelData::canPublish() const {
|| (adminRights() & AdminRight::PostMessages); || (adminRights() & AdminRight::PostMessages);
} }
bool ChannelData::canWrite() const { bool ChannelData::canWrite(bool checkForForum) const {
// Duplicated in Data::CanWriteValue(). // Duplicated in Data::CanWriteValue().
const auto allowed = amIn() const auto allowed = amIn()
|| ((flags() & Flag::HasLink) && !(flags() & Flag::JoinToWrite)); || ((flags() & Flag::HasLink) && !(flags() & Flag::JoinToWrite));
return allowed && (canPublish() const auto forumRestriction = checkForForum && isForum();
return allowed
&& !forumRestriction
&& (canPublish()
|| (!isBroadcast() || (!isBroadcast()
&& !amRestricted(Restriction::SendMessages))); && !amRestricted(Restriction::SendMessages)));
} }

View file

@ -313,7 +313,7 @@ public:
void setDefaultRestrictions(ChatRestrictions rights); void setDefaultRestrictions(ChatRestrictions rights);
// Like in ChatData. // Like in ChatData.
[[nodiscard]] bool canWrite() const; [[nodiscard]] bool canWrite(bool checkForForum = true) const;
[[nodiscard]] bool allowsForwarding() const; [[nodiscard]] bool allowsForwarding() const;
[[nodiscard]] bool canEditInformation() const; [[nodiscard]] bool canEditInformation() const;
[[nodiscard]] bool canEditPermissions() const; [[nodiscard]] bool canEditPermissions() const;

View file

@ -160,6 +160,8 @@ void Forum::applyReceivedTopics(const MTPmessages_ForumTopics &result) {
} }
void Forum::applyTopicDeleted(MsgId rootId) { void Forum::applyTopicDeleted(MsgId rootId) {
_topicsDeleted.emplace(rootId);
const auto i = _topics.find(rootId); const auto i = _topics.find(rootId);
if (i != end(_topics)) { if (i != end(_topics)) {
const auto raw = i->second.get(); const auto raw = i->second.get();
@ -192,6 +194,7 @@ void Forum::applyReceivedTopics(
} }
applyTopicDeleted(rootId); applyTopicDeleted(rootId);
}, [&](const MTPDforumTopic &data) { }, [&](const MTPDforumTopic &data) {
_topicsDeleted.remove(rootId);
const auto i = _topics.find(rootId); const auto i = _topics.find(rootId);
const auto creating = (i == end(_topics)); const auto creating = (i == end(_topics));
const auto raw = creating const auto raw = creating
@ -410,6 +413,10 @@ ForumTopic *Forum::enforceTopicFor(MsgId rootId) {
return result; return result;
} }
bool Forum::topicDeleted(MsgId rootId) const {
return _topicsDeleted.contains(rootId);
}
rpl::producer<> Forum::chatsListChanges() const { rpl::producer<> Forum::chatsListChanges() const {
return _chatsListChanges.events(); return _chatsListChanges.events();
} }

View file

@ -55,6 +55,7 @@ public:
void applyTopicDeleted(MsgId rootId); void applyTopicDeleted(MsgId rootId);
[[nodiscard]] ForumTopic *topicFor(MsgId rootId); [[nodiscard]] ForumTopic *topicFor(MsgId rootId);
[[nodiscard]] ForumTopic *enforceTopicFor(MsgId rootId); [[nodiscard]] ForumTopic *enforceTopicFor(MsgId rootId);
[[nodiscard]] bool topicDeleted(MsgId rootId) const;
void applyReceivedTopics(const MTPmessages_ForumTopics &topics); void applyReceivedTopics(const MTPmessages_ForumTopics &topics);
@ -90,6 +91,7 @@ private:
const not_null<History*> _history; const not_null<History*> _history;
base::flat_map<MsgId, std::unique_ptr<ForumTopic>> _topics; base::flat_map<MsgId, std::unique_ptr<ForumTopic>> _topics;
base::flat_set<MsgId> _topicsDeleted;
rpl::event_stream<not_null<ForumTopic*>> _topicDestroyed; rpl::event_stream<not_null<ForumTopic*>> _topicDestroyed;
Dialogs::MainList _topicsList; Dialogs::MainList _topicsList;

View file

@ -205,6 +205,13 @@ bool ForumTopic::my() const {
return (_flags & Flag::My); return (_flags & Flag::My);
} }
bool ForumTopic::canWrite() const {
const auto channel = this->channel();
return channel->amIn()
&& !channel->amRestricted(ChatRestriction::SendMessages)
&& (!closed() || canToggleClosed());
}
bool ForumTopic::canEdit() const { bool ForumTopic::canEdit() const {
return my() || channel()->canManageTopics(); return my() || channel()->canManageTopics();
} }

View file

@ -62,6 +62,7 @@ public:
[[nodiscard]] MsgId rootId() const; [[nodiscard]] MsgId rootId() const;
[[nodiscard]] bool my() const; [[nodiscard]] bool my() const;
[[nodiscard]] bool canWrite() const;
[[nodiscard]] bool canEdit() const; [[nodiscard]] bool canEdit() const;
[[nodiscard]] bool canToggleClosed() const; [[nodiscard]] bool canToggleClosed() const;
[[nodiscard]] bool canTogglePinned() const; [[nodiscard]] bool canTogglePinned() const;

View file

@ -937,11 +937,11 @@ Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const {
} }
bool PeerData::canWrite() const { bool PeerData::canWrite(bool checkForForum) const {
if (const auto user = asUser()) { if (const auto user = asUser()) {
return user->canWrite(); return user->canWrite();
} else if (const auto channel = asChannel()) { } else if (const auto channel = asChannel()) {
return channel->canWrite(); return channel->canWrite(checkForForum);
} else if (const auto chat = asChat()) { } else if (const auto chat = asChat()) {
return chat->canWrite(); return chat->canWrite();
} }

View file

@ -207,7 +207,7 @@ public:
return _notify; return _notify;
} }
[[nodiscard]] bool canWrite() const; [[nodiscard]] bool canWrite(bool checkForForum = true) const;
[[nodiscard]] bool allowsForwarding() const; [[nodiscard]] bool allowsForwarding() const;
[[nodiscard]] Data::RestrictionCheckResult amRestricted( [[nodiscard]] Data::RestrictionCheckResult amRestricted(
ChatRestriction right) const; ChatRestriction right) const;

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_message_reactions.h" #include "data/data_message_reactions.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -201,10 +202,11 @@ rpl::producer<bool> CanWriteValue(ChatData *chat) {
}); });
} }
rpl::producer<bool> CanWriteValue(ChannelData *channel) { rpl::producer<bool> CanWriteValue(ChannelData *channel, bool checkForForum) {
using Flag = ChannelDataFlag; using Flag = ChannelDataFlag;
const auto mask = 0 const auto mask = 0
| Flag::Left | Flag::Left
| Flag::Forum
| Flag::JoinToWrite | Flag::JoinToWrite
| Flag::HasLink | Flag::HasLink
| Flag::Forbidden | Flag::Forbidden
@ -221,15 +223,19 @@ rpl::producer<bool> CanWriteValue(ChannelData *channel) {
DefaultRestrictionValue( DefaultRestrictionValue(
channel, channel,
ChatRestriction::SendMessages), ChatRestriction::SendMessages),
[]( [=](
ChannelDataFlags flags, ChannelDataFlags flags,
bool postMessagesRight, bool postMessagesRight,
bool sendMessagesRestriction, bool sendMessagesRestriction,
bool defaultSendMessagesRestriction) { bool defaultSendMessagesRestriction) {
const auto notAmInFlags = Flag::Left | Flag::Forbidden; const auto notAmInFlags = Flag::Left | Flag::Forbidden;
const auto forumRestriction = checkForForum
&& (flags & Flag::Forum);
const auto allowed = !(flags & notAmInFlags) const auto allowed = !(flags & notAmInFlags)
|| ((flags & Flag::HasLink) && !(flags & Flag::JoinToWrite)); || ((flags & Flag::HasLink) && !(flags & Flag::JoinToWrite));
return allowed && (postMessagesRight return allowed
&& !forumRestriction
&& (postMessagesRight
|| (flags & Flag::Creator) || (flags & Flag::Creator)
|| (!(flags & Flag::Broadcast) || (!(flags & Flag::Broadcast)
&& !sendMessagesRestriction && !sendMessagesRestriction
@ -237,17 +243,52 @@ rpl::producer<bool> CanWriteValue(ChannelData *channel) {
}); });
} }
rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer) { rpl::producer<bool> CanWriteValue(
not_null<PeerData*> peer,
bool checkForForum) {
if (auto user = peer->asUser()) { if (auto user = peer->asUser()) {
return CanWriteValue(user); return CanWriteValue(user);
} else if (auto chat = peer->asChat()) { } else if (auto chat = peer->asChat()) {
return CanWriteValue(chat); return CanWriteValue(chat);
} else if (auto channel = peer->asChannel()) { } else if (auto channel = peer->asChannel()) {
return CanWriteValue(channel); return CanWriteValue(channel, checkForForum);
} }
Unexpected("Bad peer value in CanWriteValue"); Unexpected("Bad peer value in CanWriteValue");
} }
rpl::producer<bool> CanWriteValue(not_null<ForumTopic*> topic) {
using Flag = ChannelDataFlag;
const auto mask = 0
| Flag::Left
| Flag::JoinToWrite
| Flag::Forum
| Flag::Forbidden;
const auto channel = topic->channel();
return rpl::combine(
PeerFlagsValue(channel.get(), mask),
RestrictionValue(
channel,
ChatRestriction::SendMessages),
DefaultRestrictionValue(
channel,
ChatRestriction::SendMessages),
topic->session().changes().topicFlagsValue(
topic,
TopicUpdate::Flag::Closed),
[=](
ChannelDataFlags flags,
bool sendMessagesRestriction,
bool defaultSendMessagesRestriction,
auto) {
const auto notAmInFlags = Flag::Left | Flag::Forbidden;
const auto allowed = !(flags & notAmInFlags);
return allowed
&& !sendMessagesRestriction
&& !defaultSendMessagesRestriction
&& (!topic->closed() || topic->canToggleClosed());
});
}
// This is duplicated in PeerData::canPinMessages(). // This is duplicated in PeerData::canPinMessages().
rpl::producer<bool> CanPinMessagesValue(not_null<PeerData*> peer) { rpl::producer<bool> CanPinMessagesValue(not_null<PeerData*> peer) {
using namespace rpl::mappers; using namespace rpl::mappers;

View file

@ -21,6 +21,7 @@ class Session;
namespace Data { namespace Data {
struct Reaction; struct Reaction;
class ForumTopic;
template <typename ChangeType, typename Error, typename Generator> template <typename ChangeType, typename Error, typename Generator>
inline auto FlagsValueWithMask( inline auto FlagsValueWithMask(
@ -102,8 +103,13 @@ inline auto PeerFullFlagValue(
[[nodiscard]] rpl::producer<bool> CanWriteValue(UserData *user); [[nodiscard]] rpl::producer<bool> CanWriteValue(UserData *user);
[[nodiscard]] rpl::producer<bool> CanWriteValue(ChatData *chat); [[nodiscard]] rpl::producer<bool> CanWriteValue(ChatData *chat);
[[nodiscard]] rpl::producer<bool> CanWriteValue(ChannelData *channel); [[nodiscard]] rpl::producer<bool> CanWriteValue(
[[nodiscard]] rpl::producer<bool> CanWriteValue(not_null<PeerData*> peer); ChannelData *channel,
bool checkForForum = true);
[[nodiscard]] rpl::producer<bool> CanWriteValue(
not_null<PeerData*> peer,
bool checkForForum = true);
[[nodiscard]] rpl::producer<bool> CanWriteValue(not_null<ForumTopic*> topic);
[[nodiscard]] rpl::producer<bool> CanPinMessagesValue( [[nodiscard]] rpl::producer<bool> CanPinMessagesValue(
not_null<PeerData*> peer); not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<bool> CanManageGroupCallValue( [[nodiscard]] rpl::producer<bool> CanManageGroupCallValue(

View file

@ -1133,6 +1133,9 @@ void History::applyServiceChanges(
if (const auto icon = data.vicon_emoji_id()) { if (const auto icon = data.vicon_emoji_id()) {
topic->applyIconId(icon->v); topic->applyIconId(icon->v);
} }
if (const auto closed = data.vclosed()) {
topic->setClosed(mtpIsTrue(*closed));
}
} }
}, [](const auto &) { }, [](const auto &) {
}); });

View file

@ -76,6 +76,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_message_reactions.h" #include "data/data_message_reactions.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_forum_topic.h"
#include "data/data_poll.h" #include "data/data_poll.h"
#include "data/data_photo.h" #include "data/data_photo.h"
#include "data/data_photo_media.h" #include "data/data_photo_media.h"
@ -2003,7 +2004,6 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
return; return;
} }
auto selectedState = getSelectionState(); auto selectedState = getSelectionState();
auto canSendMessages = _peer->canWrite();
// -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items // -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items
auto isUponSelected = 0; auto isUponSelected = 0;
@ -2079,7 +2079,18 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
return; return;
} }
const auto itemId = item->fullId(); const auto itemId = item->fullId();
if (canSendMessages) { const auto canReply = [&] {
const auto peer = item->history()->peer;
if (const auto forum = item->history()->peer->forum()) {
const auto topicRootId = item->topicRootId();
const auto topic = item->topic();
return topic
? topic->canWrite()
: peer->canWrite(!topicRootId);
}
return peer->canWrite();
}();
if (canReply) {
_menu->addAction(tr::lng_context_reply_msg(tr::now), [=] { _menu->addAction(tr::lng_context_reply_msg(tr::now), [=] {
_widget->replyToMessage(itemId); _widget->replyToMessage(itemId);
}, &st::menuIconReply); }, &st::menuIconReply);

View file

@ -798,7 +798,13 @@ bool HistoryItem::canBeEdited() const {
if (isPost() && channel->canEditMessages()) { if (isPost() && channel->canEditMessages()) {
return true; return true;
} else if (out()) { } else if (out()) {
return isPost() ? channel->canPublish() : channel->canWrite(); if (isPost()) {
return channel->canPublish();
} else if (const auto topic = this->topic()) {
return topic->canWrite();
} else {
return channel->canWrite();
}
} else { } else {
return false; return false;
} }

View file

@ -107,44 +107,52 @@ namespace {
QString GetErrorTextForSending( QString GetErrorTextForSending(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const HistoryItemsList &items, SendingErrorRequest request) {
const TextWithTags &comment, const auto forum = request.topicRootId ? peer->forum() : nullptr;
bool ignoreSlowmodeCountdown) { const auto topic = forum
if (!peer->canWrite()) { ? forum->topicFor(request.topicRootId)
: nullptr;
if (!(topic ? topic->canWrite() : peer->canWrite())) {
return tr::lng_forward_cant(tr::now); return tr::lng_forward_cant(tr::now);
} }
for (const auto &item : items) { if (request.forward) {
if (const auto media = item->media()) { for (const auto &item : *request.forward) {
const auto error = media->errorTextForForward(peer); if (const auto media = item->media()) {
if (!error.isEmpty() && error != qstr("skip")) { const auto error = media->errorTextForForward(peer);
return error; if (!error.isEmpty() && error != qstr("skip")) {
return error;
}
} }
} }
} }
const auto error = Data::RestrictionError( const auto error = Data::RestrictionError(
peer, peer,
ChatRestriction::SendInline); ChatRestriction::SendInline);
if (error && HasInlineItems(items)) { if (error && request.forward && HasInlineItems(*request.forward)) {
return *error; return *error;
} }
if (peer->slowmodeApplied()) { if (peer->slowmodeApplied()) {
const auto hasText = (request.text && !request.text->empty());
const auto count = (hasText ? 1 : 0)
+ (request.forward ? int(request.forward->size()) : 0);
if (const auto history = peer->owner().historyLoaded(peer)) { if (const auto history = peer->owner().historyLoaded(peer)) {
if (!ignoreSlowmodeCountdown if (!request.ignoreSlowmodeCountdown
&& (history->latestSendingMessage() != nullptr) && (history->latestSendingMessage() != nullptr)
&& (!items.empty() || !comment.text.isEmpty())) { && (count > 0)) {
return tr::lng_slowmode_no_many(tr::now); return tr::lng_slowmode_no_many(tr::now);
} }
} }
if (comment.text.size() > MaxMessageSize) { if (request.text && request.text->text.size() > MaxMessageSize) {
return tr::lng_slowmode_too_long(tr::now); return tr::lng_slowmode_too_long(tr::now);
} else if (!items.empty() && !comment.text.isEmpty()) { } else if (hasText && count > 1) {
return tr::lng_slowmode_no_many(tr::now); return tr::lng_slowmode_no_many(tr::now);
} else if (items.size() > 1) { } else if (count > 1) {
const auto albumForward = [&] { const auto albumForward = [&] {
if (const auto groupId = items.front()->groupId()) { const auto first = request.forward->front();
for (const auto &item : items) { if (const auto groupId = first->groupId()) {
for (const auto &item : *request.forward) {
if (item->groupId() != groupId) { if (item->groupId() != groupId) {
return false; return false;
} }
@ -159,7 +167,7 @@ QString GetErrorTextForSending(
} }
} }
if (const auto left = peer->slowmodeSecondsLeft()) { if (const auto left = peer->slowmodeSecondsLeft()) {
if (!ignoreSlowmodeCountdown) { if (!request.ignoreSlowmodeCountdown) {
return tr::lng_slowmode_enabled( return tr::lng_slowmode_enabled(
tr::now, tr::now,
lt_left, lt_left,
@ -242,13 +250,6 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
return MTPMessageReplyHeader(); return MTPMessageReplyHeader();
} }
QString GetErrorTextForSending(
not_null<PeerData*> peer,
const HistoryItemsList &items,
bool ignoreSlowmodeCountdown) {
return GetErrorTextForSending(peer, items, {}, ignoreSlowmodeCountdown);
}
TextWithEntities DropCustomEmoji(TextWithEntities text) { TextWithEntities DropCustomEmoji(TextWithEntities text) {
text.entities.erase( text.entities.erase(
ranges::remove( ranges::remove(

View file

@ -40,15 +40,17 @@ void RequestDependentMessageData(
MsgId replyToId); MsgId replyToId);
[[nodiscard]] MTPMessageReplyHeader NewMessageReplyHeader( [[nodiscard]] MTPMessageReplyHeader NewMessageReplyHeader(
const Api::SendAction &action); const Api::SendAction &action);
struct SendingErrorRequest {
MsgId topicRootId = 0;
const HistoryItemsList *forward = nullptr;
const TextWithTags *text = nullptr;
bool ignoreSlowmodeCountdown = false;
};
[[nodiscard]] QString GetErrorTextForSending( [[nodiscard]] QString GetErrorTextForSending(
not_null<PeerData*> peer, not_null<PeerData*> peer,
const HistoryItemsList &items, SendingErrorRequest request);
bool ignoreSlowmodeCountdown = false);
[[nodiscard]] QString GetErrorTextForSending(
not_null<PeerData*> peer,
const HistoryItemsList &items,
const TextWithTags &comment,
bool ignoreSlowmodeCountdown = false);
[[nodiscard]] TextWithEntities DropCustomEmoji(TextWithEntities text); [[nodiscard]] TextWithEntities DropCustomEmoji(TextWithEntities text);
class HistoryMessage final : public HistoryItem { class HistoryMessage final : public HistoryItem {

View file

@ -65,6 +65,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_scheduled_messages.h" #include "data/data_scheduled_messages.h"
@ -1902,8 +1904,8 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
auto fieldWillBeHiddenAfterEdit = (!fieldAvailable && _editMsgId != 0); auto fieldWillBeHiddenAfterEdit = (!fieldAvailable && _editMsgId != 0);
clearFieldText(0, fieldHistoryAction); clearFieldText(0, fieldHistoryAction);
_field->setFocus(); _field->setFocus();
_replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_replyToId = 0; _processingReplyId = _replyToId = 0;
setEditMsgId(0); setEditMsgId(0);
if (fieldWillBeHiddenAfterEdit) { if (fieldWillBeHiddenAfterEdit) {
updateControlsVisibility(); updateControlsVisibility();
@ -1926,23 +1928,25 @@ void HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
_parsedLinks = _fieldLinksParser->list().current(); _parsedLinks = _fieldLinksParser->list().current();
_previewState = draft->previewState; _previewState = draft->previewState;
_replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_processingReplyId = _replyToId = 0;
if (const auto editDraft = _history->localEditDraft({})) { if (const auto editDraft = _history->localEditDraft({})) {
setEditMsgId(editDraft->msgId); setEditMsgId(editDraft->msgId);
_replyToId = 0;
} else { } else {
_replyToId = readyToForward() ? 0 : _history->localDraft({})->msgId;
setEditMsgId(0); setEditMsgId(0);
} }
updateCmdStartShown(); updateCmdStartShown();
updateControlsVisibility(); updateControlsVisibility();
updateControlsGeometry(); updateControlsGeometry();
refreshTopBarActiveChat(); refreshTopBarActiveChat();
if (_editMsgId || _replyToId) { if (_editMsgId) {
updateReplyEditTexts(); updateReplyEditTexts();
if (!_replyEditMsg) { if (!_replyEditMsg) {
requestMessageData(_editMsgId ? _editMsgId : _replyToId); requestMessageData(_editMsgId);
} }
} else if (!readyToForward()) {
_processingReplyId = _history->localDraft({})->msgId;
processReply();
} }
} }
@ -2105,8 +2109,8 @@ void HistoryWidget::showHistory(
HistoryView::Element::ClearGlobal(); HistoryView::Element::ClearGlobal();
_saveEditMsgRequestId = 0; _saveEditMsgRequestId = 0;
_replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_editMsgId = _replyToId = 0; _processingReplyId = _editMsgId = _replyToId = 0;
_previewData = nullptr; _previewData = nullptr;
_previewCache.clear(); _previewCache.clear();
_fieldBarCancel->hide(); _fieldBarCancel->hide();
@ -2503,7 +2507,7 @@ void HistoryWidget::setupScheduledToggle() {
void HistoryWidget::refreshScheduledToggle() { void HistoryWidget::refreshScheduledToggle() {
const auto has = _history const auto has = _history
&& _peer->canWrite() && _canSendMessages
&& (session().data().scheduledMessages().count(_history) > 0); && (session().data().scheduledMessages().count(_history) > 0);
if (!_scheduled && has) { if (!_scheduled && has) {
_scheduled.create(this, st::historyScheduledToggle); _scheduled.create(this, st::historyScheduledToggle);
@ -2564,9 +2568,15 @@ bool HistoryWidget::canWriteMessage() const {
} }
std::optional<QString> HistoryWidget::writeRestriction() const { std::optional<QString> HistoryWidget::writeRestriction() const {
return _peer auto result = _peer
? Data::RestrictionError(_peer, ChatRestriction::SendMessages) ? Data::RestrictionError(_peer, ChatRestriction::SendMessages)
: std::nullopt; : std::nullopt;
if (result) {
return result;
} else if (_peer && _peer->isForum()) {
return u"You can reply to messages in topics."_q;
}
return std::nullopt;
} }
void HistoryWidget::updateControlsVisibility() { void HistoryWidget::updateControlsVisibility() {
@ -3699,11 +3709,17 @@ void HistoryWidget::send(Api::SendOptions options) {
message.webPageId = webPageId; message.webPageId = webPageId;
if (_canSendMessages) { if (_canSendMessages) {
const auto topicRootId = _replyEditMsg
? _replyEditMsg->topicRootId()
: 0;
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
_peer, _peer,
_toForward.items, {
message.textWithTags, .topicRootId = topicRootId,
options.scheduled); .forward = &_toForward.items,
.text = &message.textWithTags,
.ignoreSlowmodeCountdown = (options.scheduled != 0),
});
if (!error.isEmpty()) { if (!error.isEmpty()) {
Ui::ShowMultilineToast({ Ui::ShowMultilineToast({
.parentOverride = Window::Show(controller()).toastParent(), .parentOverride = Window::Show(controller()).toastParent(),
@ -4027,7 +4043,7 @@ void HistoryWidget::chooseAttach(
return; return;
} }
if (!_peer || !_peer->canWrite()) { if (!_peer || !_canSendMessages) {
return; return;
} else if (const auto error = Data::RestrictionError( } else if (const auto error = Data::RestrictionError(
_peer, _peer,
@ -5248,6 +5264,10 @@ void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
if (item == _replyEditMsg && _replyToId) { if (item == _replyEditMsg && _replyToId) {
cancelReply(); cancelReply();
} }
if (item == _processingReplyItem) {
_processingReplyId = 0;
_processingReplyItem = nullptr;
}
if (_kbReplyTo && item == _kbReplyTo) { if (_kbReplyTo && item == _kbReplyTo) {
toggleKeyboard(); toggleKeyboard();
_kbReplyTo = nullptr; _kbReplyTo = nullptr;
@ -6080,7 +6100,7 @@ void HistoryWidget::fieldTabbed() {
} }
void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) {
if (!_peer || !_peer->canWrite()) { if (!_peer || !_canSendMessages) {
return; return;
} else if (showSlowmodeError()) { } else if (showSlowmodeError()) {
return; return;
@ -6547,7 +6567,7 @@ bool HistoryWidget::sendExistingDocument(
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
return false; return false;
} else if (!_peer } else if (!_peer
|| !_peer->canWrite() || !_canSendMessages
|| showSlowmodeError() || showSlowmodeError()
|| ShowSendPremiumError(controller(), document)) { || ShowSendPremiumError(controller(), document)) {
return false; return false;
@ -6583,7 +6603,7 @@ bool HistoryWidget::sendExistingPhoto(
Ui::MakeInformBox(*error), Ui::MakeInformBox(*error),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
return false; return false;
} else if (!_peer || !_peer->canWrite()) { } else if (!_peer || !_canSendMessages) {
return false; return false;
} else if (showSlowmodeError()) { } else if (showSlowmodeError()) {
return false; return false;
@ -6657,24 +6677,80 @@ void HistoryWidget::replyToMessage(FullMsgId itemId) {
} }
void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) { void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) {
if (!item->isRegular() || !_canSendMessages) { _processingReplyId = item->id;
_processingReplyItem = item;
processReply();
}
void HistoryWidget::processReply() {
const auto processContinue = [=] {
return crl::guard(_list, [=] {
if (!_peer || !_processingReplyId) {
return;
} else if (!_processingReplyItem) {
_processingReplyItem = _peer->owner().message(
_peer,
_processingReplyId);
if (!_processingReplyItem) {
_processingReplyId = 0;
} else {
processReply();
}
}
});
};
const auto processCancel = [=] {
_processingReplyId = 0;
_processingReplyItem = nullptr;
};
if (!_peer || !_processingReplyId) {
return processCancel();
} else if (!_processingReplyItem) {
session().api().requestMessageData(
_peer,
_processingReplyId,
processContinue());
return; return;
} else if (item->history() == _migrated) { } else if (_processingReplyItem->history() == _migrated) {
if (item->isService()) { if (_processingReplyItem->isService()) {
controller()->show(Ui::MakeInformBox(tr::lng_reply_cant())); controller()->show(Ui::MakeInformBox(tr::lng_reply_cant()));
} else { } else {
const auto itemId = item->fullId(); const auto itemId = _processingReplyItem->fullId();
controller()->show( controller()->show(
Ui::MakeConfirmBox({ Ui::MakeConfirmBox({
.text = tr::lng_reply_cant_forward(), .text = tr::lng_reply_cant_forward(),
.confirmed = crl::guard(this, [=] { .confirmed = crl::guard(this, [=] {
controller()->content()->setForwardDraft( controller()->content()->setForwardDraft(
_peer->id, _peer->id,
{ .ids = { 1, itemId } }); {.ids = { 1, itemId } });
}), }),
.confirmText = tr::lng_selected_forward(), .confirmText = tr::lng_selected_forward(),
})); }));
} }
return processCancel();
} else if (_processingReplyItem->history() != _history
|| !_processingReplyItem->isRegular()) {
return processCancel();
} else if (const auto forum = _peer->forum()) {
const auto topicRootId = _processingReplyItem->topicRootId();
if (!topicRootId || forum->topicDeleted(topicRootId)) {
return processCancel();
} else if (const auto topic = forum->topicFor(topicRootId)) {
if (!topic->canWrite()) {
return processCancel();
}
} else {
forum->requestTopic(topicRootId, processContinue());
}
} else if (!_peer->canWrite()) {
return processCancel();
}
setReplyFieldsFromProcessing();
}
void HistoryWidget::setReplyFieldsFromProcessing() {
if (!_processingReplyId || !_processingReplyItem) {
return; return;
} }
@ -6683,23 +6759,27 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) {
_composeSearch->hideAnimated(); _composeSearch->hideAnimated();
} }
const auto id = base::take(_processingReplyId);
const auto item = base::take(_processingReplyItem);
if (_editMsgId) { if (_editMsgId) {
if (const auto localDraft = _history->localDraft({})) { if (const auto localDraft = _history->localDraft({})) {
localDraft->msgId = item->id; localDraft->msgId = id;
} else { } else {
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
TextWithTags(), TextWithTags(),
item->id, id,
MsgId(), // topicRootId MsgId(),
MessageCursor(), MessageCursor(),
Data::PreviewState::Allowed)); Data::PreviewState::Allowed));
} }
} else { } else {
_replyEditMsg = item; _replyEditMsg = item;
_replyToId = item->id; _replyToId = id;
updateReplyEditText(_replyEditMsg); updateReplyEditText(_replyEditMsg);
updateCanSendMessage();
updateBotKeyboard(); updateBotKeyboard();
updateReplyToName(); updateReplyToName();
updateControlsVisibility();
updateControlsGeometry(); updateControlsGeometry();
updateField(); updateField();
refreshTopBarActiveChat(); refreshTopBarActiveChat();
@ -6709,7 +6789,9 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) {
_saveDraftStart = crl::now(); _saveDraftStart = crl::now();
saveDraft(); saveDraft();
_field->setFocus(); if (!_field->isHidden()) {
_field->setFocus();
}
} }
void HistoryWidget::editMessage(FullMsgId itemId) { void HistoryWidget::editMessage(FullMsgId itemId) {
@ -6836,16 +6918,17 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
if (_replyToId) { if (_replyToId) {
wasReply = true; wasReply = true;
_replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_replyToId = 0; _processingReplyId = _replyToId = 0;
mouseMoveEvent(0); mouseMoveEvent(0);
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) { if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
updateMouseTracking(); updateMouseTracking();
} }
updateBotKeyboard(); updateBotKeyboard();
refreshTopBarActiveChat(); refreshTopBarActiveChat();
updateCanSendMessage();
updateControlsVisibility();
updateControlsGeometry(); updateControlsGeometry();
update(); update();
} else if (const auto localDraft = (_history ? _history->localDraft({}) : nullptr)) { } else if (const auto localDraft = (_history ? _history->localDraft({}) : nullptr)) {
@ -7085,14 +7168,7 @@ void HistoryWidget::updatePreview() {
void HistoryWidget::fullInfoUpdated() { void HistoryWidget::fullInfoUpdated() {
auto refresh = false; auto refresh = false;
if (_list) { if (_list) {
auto newCanSendMessages = _peer->canWrite(); if (updateCanSendMessage()) {
if (newCanSendMessages != _canSendMessages) {
_canSendMessages = newCanSendMessages;
if (!_canSendMessages) {
cancelReply();
}
refreshScheduledToggle();
refreshSilentToggle();
refresh = true; refresh = true;
} }
checkFieldAutocomplete(); checkFieldAutocomplete();
@ -7134,14 +7210,7 @@ void HistoryWidget::handlePeerUpdate() {
|| (!isBlocked() && _joinChannel->isHidden() == isJoinChannel())) { || (!isBlocked() && _joinChannel->isHidden() == isJoinChannel())) {
resize = true; resize = true;
} }
bool newCanSendMessages = _peer->canWrite(); if (updateCanSendMessage()) {
if (newCanSendMessages != _canSendMessages) {
_canSendMessages = newCanSendMessages;
if (!_canSendMessages) {
cancelReply();
}
refreshScheduledToggle();
refreshSilentToggle();
resize = true; resize = true;
} }
updateControlsVisibility(); updateControlsVisibility();
@ -7151,6 +7220,24 @@ void HistoryWidget::handlePeerUpdate() {
} }
} }
bool HistoryWidget::updateCanSendMessage() {
const auto replyTo = (_replyToId && !_editMsgId) ? _replyEditMsg : 0;
const auto topic = replyTo ? replyTo->topic() : nullptr;
const auto newCanSendMessages = topic
? topic->canWrite()
: _peer->canWrite();
if (_canSendMessages == newCanSendMessages) {
return false;
}
_canSendMessages = newCanSendMessages;
if (!_canSendMessages) {
cancelReply();
}
refreshScheduledToggle();
refreshSilentToggle();
return true;
}
void HistoryWidget::forwardSelected() { void HistoryWidget::forwardSelected() {
if (!_list) { if (!_list) {
return; return;

View file

@ -332,6 +332,8 @@ private:
bool cornerButtonsHas(HistoryView::CornerButtonType type) override; bool cornerButtonsHas(HistoryView::CornerButtonType type) override;
void checkSuggestToGigagroup(); void checkSuggestToGigagroup();
void processReply();
void setReplyFieldsFromProcessing();
void initTabbedSelector(); void initTabbedSelector();
void initVoiceRecordBar(); void initVoiceRecordBar();
@ -382,6 +384,7 @@ private:
void toggleTabbedSelectorMode(); void toggleTabbedSelectorMode();
void recountChatWidth(); void recountChatWidth();
void handlePeerUpdate(); void handlePeerUpdate();
bool updateCanSendMessage();
void setMembersShowAreaActive(bool active); void setMembersShowAreaActive(bool active);
void handleHistoryChange(not_null<const History*> history); void handleHistoryChange(not_null<const History*> history);
void showAboutTopPromotion(); void showAboutTopPromotion();
@ -616,6 +619,9 @@ private:
Ui::Text::String _replyToName; Ui::Text::String _replyToName;
int _replyToNameVersion = 0; int _replyToNameVersion = 0;
MsgId _processingReplyId = 0;
HistoryItem *_processingReplyItem = nullptr;
Data::ResolvedForwardDraft _toForward; Data::ResolvedForwardDraft _toForward;
Ui::Text::String _toForwardFrom, _toForwardText; Ui::Text::String _toForwardFrom, _toForwardText;
int _toForwardNameVersion = 0; int _toForwardNameVersion = 0;

View file

@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_photo_media.h" #include "data/data_photo_media.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_groups.h" #include "data/data_groups.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -584,9 +585,11 @@ bool AddReplyToMessageAction(
not_null<ListWidget*> list) { not_null<ListWidget*> list) {
const auto context = list->elementContext(); const auto context = list->elementContext();
const auto item = request.item; const auto item = request.item;
const auto topic = item ? item->topic() : nullptr;
const auto peer = item ? item->history()->peer.get() : nullptr;
if (!item if (!item
|| !item->isRegular() || !item->isRegular()
|| !item->history()->peer->canWrite() || (topic ? topic->canWrite() : !peer->canWrite())
|| (context != Context::History && context != Context::Replies)) { || (context != Context::History && context != Context::Replies)) {
return false; return false;
} }

View file

@ -2703,7 +2703,7 @@ bool Message::hasFastReply() const {
bool Message::displayFastReply() const { bool Message::displayFastReply() const {
return hasFastReply() return hasFastReply()
&& data()->isRegular() && data()->isRegular()
&& data()->history()->peer->canWrite() && data()->history()->peer->canWrite(false)
&& !delegate()->elementInSelectionMode(); && !delegate()->elementInSelectionMode();
} }

View file

@ -609,9 +609,11 @@ void RepliesWidget::setupComposeControls() {
ChatRestriction::SendMessages); ChatRestriction::SendMessages);
return restriction return restriction
? restriction ? restriction
: _history->peer->canWrite() : topicRestriction
? std::move(topicRestriction) ? std::move(topicRestriction)
: tr::lng_group_not_accessible(tr::now); : !(_topic ? _topic->canWrite() : _history->peer->canWrite())
? tr::lng_group_not_accessible(tr::now)
: std::optional<QString>();
}); });
_composeControls->setHistory({ _composeControls->setHistory({
@ -1217,7 +1219,10 @@ void RepliesWidget::refreshJoinGroupButton() {
} }
}; };
const auto channel = _history->peer->asChannel(); const auto channel = _history->peer->asChannel();
if (channel->amIn() || channel->canWrite()) { const auto canWrite = !channel->isForum()
? channel->canWrite()
: (_topic && _topic->canWrite());
if (channel->amIn() || canWrite) {
set(nullptr); set(nullptr);
} else { } else {
if (!_joinGroup) { if (!_joinGroup) {

View file

@ -129,7 +129,7 @@ void ShowChooseBox(
callback(peer); callback(peer);
}; };
auto filter = [=](not_null<PeerData*> peer) -> bool { auto filter = [=](not_null<PeerData*> peer) -> bool {
if (!peer->canWrite()) { if (!peer->canWrite()) { // #TODO forum forward
return false; return false;
} else if (const auto user = peer->asUser()) { } else if (const auto user = peer->asUser()) {
if (user->isBot()) { if (user->isBot()) {

View file

@ -43,7 +43,7 @@ SendAsPeers::SendAsPeers(not_null<Session*> session)
bool SendAsPeers::shouldChoose(not_null<PeerData*> peer) { bool SendAsPeers::shouldChoose(not_null<PeerData*> peer) {
refresh(peer); refresh(peer);
return peer->canWrite() && (list(peer).size() > 1); return peer->canWrite(false) && (list(peer).size() > 1);
} }
void SendAsPeers::refresh(not_null<PeerData*> peer, bool force) { void SendAsPeers::refresh(not_null<PeerData*> peer, bool force) {

View file

@ -335,7 +335,10 @@ MainWidget::MainWidget(
_controller->activeChatValue( _controller->activeChatValue(
) | rpl::map([](Dialogs::Key key) { ) | rpl::map([](Dialogs::Key key) {
const auto peer = key.peer(); const auto peer = key.peer();
auto canWrite = peer const auto topic = key.topic();
auto canWrite = topic
? Data::CanWriteValue(topic)
: peer
? Data::CanWriteValue(peer) ? Data::CanWriteValue(peer)
: rpl::single(false); : rpl::single(false);
return std::move( return std::move(
@ -516,10 +519,10 @@ bool MainWidget::setForwardDraft(PeerId peerId, Data::ForwardDraft &&draft) {
Expects(peerId != 0); Expects(peerId != 0);
const auto peer = session().data().peer(peerId); const auto peer = session().data().peer(peerId);
const auto items = session().data().idsToItems(draft.ids);
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
peer, peer, // #TODO forum forward
session().data().idsToItems(draft.ids), { .forward = &items, .ignoreSlowmodeCountdown = true });
true);
if (!error.isEmpty()) { if (!error.isEmpty()) {
Ui::show(Ui::MakeInformBox(error), Ui::LayerOption::KeepOther); Ui::show(Ui::MakeInformBox(error), Ui::LayerOption::KeepOther);
return false; return false;
@ -541,7 +544,7 @@ bool MainWidget::shareUrl(
Expects(peerId != 0); Expects(peerId != 0);
const auto peer = session().data().peer(peerId); const auto peer = session().data().peer(peerId);
if (!peer->canWrite()) { if (!peer->canWrite()) { // #TODO forum forward
_controller->show(Ui::MakeInformBox(tr::lng_share_cant())); _controller->show(Ui::MakeInformBox(tr::lng_share_cant()));
return false; return false;
} }
@ -575,7 +578,7 @@ bool MainWidget::inlineSwitchChosen(
Expects(peerId != 0); Expects(peerId != 0);
const auto peer = session().data().peer(peerId); const auto peer = session().data().peer(peerId);
if (!peer->canWrite()) { if (!peer->canWrite()) { // #TODO forum forward
Ui::show(Ui::MakeInformBox(tr::lng_inline_switch_cant())); Ui::show(Ui::MakeInformBox(tr::lng_inline_switch_cant()));
return false; return false;
} }
@ -607,7 +610,7 @@ bool MainWidget::sendPaths(PeerId peerId) {
Expects(peerId != 0); Expects(peerId != 0);
auto peer = session().data().peer(peerId); auto peer = session().data().peer(peerId);
if (!peer->canWrite()) { if (!peer->canWrite()) { // #TODO forum forward
Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant())); Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant()));
return false; return false;
} else if (const auto error = Data::RestrictionError( } else if (const auto error = Data::RestrictionError(
@ -638,7 +641,7 @@ void MainWidget::onFilesOrForwardDrop(
} }
} else { } else {
auto peer = session().data().peer(peerId); auto peer = session().data().peer(peerId);
if (!peer->canWrite()) { if (!peer->canWrite()) { // #TODO forum forward
Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant())); Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant()));
return; return;
} }

View file

@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_document_media.h" #include "data/data_document_media.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "history/history.h" #include "history/history.h"
@ -168,7 +169,9 @@ auto ActiveChat(not_null<Window::Controller*> controller) {
} }
bool CanWriteToActiveChat(not_null<Window::Controller*> controller) { bool CanWriteToActiveChat(not_null<Window::Controller*> controller) {
if (const auto history = ActiveChat(controller).history()) { if (const auto topic = ActiveChat(controller).topic()) {
return topic->canWrite();
} else if (const auto history = ActiveChat(controller).history()) {
return history->peer->canWrite(); return history->peer->canWrite();
} }
return false; return false;
@ -557,10 +560,11 @@ void AppendEmojiPacks(
controller->sessionController()->activeChatValue( controller->sessionController()->activeChatValue(
) | rpl::map([](Dialogs::Key k) { ) | rpl::map([](Dialogs::Key k) {
return k.peer() const auto topic = k.topic();
&& k.history() const auto peer = k.peer();
&& k.peer()->canWrite() return peer
&& !RestrictionToSendStickers(k.peer()); && !RestrictionToSendStickers(peer)
&& (topic ? topic->canWrite() : peer->canWrite());
}) | rpl::distinct_until_changed( }) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](bool value) { ) | rpl::start_with_next([=](bool value) {
[self dismissPopover:nil]; [self dismissPopover:nil];

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" // ApiWrap::updateStickers() #include "apiwrap.h" // ApiWrap::updateStickers()
#include "core/application.h" #include "core/application.h"
#include "data/data_peer.h" // PeerData::canWrite() #include "data/data_peer.h" // PeerData::canWrite()
#include "data/data_forum_topic.h" // Data::ForumTopic::canWrite()
#include "data/data_session.h" #include "data/data_session.h"
#include "data/stickers/data_stickers.h" // Stickers::setsRef() #include "data/stickers/data_stickers.h" // Stickers::setsRef()
#include "main/main_domain.h" #include "main/main_domain.h"
@ -144,7 +145,11 @@ const auto kAudioItemIdentifier = @"touchbarAudio";
_canApplyMarkdownLast), _canApplyMarkdownLast),
_controller->sessionController()->activeChatValue( _controller->sessionController()->activeChatValue(
) | rpl::map([](Dialogs::Key k) { ) | rpl::map([](Dialogs::Key k) {
return k.peer() && k.history() && k.peer()->canWrite(); const auto topic = k.topic();
const auto peer = k.peer();
return topic
? topic->canWrite()
: (peer && peer->canWrite());
}) | rpl::distinct_until_changed() }) | rpl::distinct_until_changed()
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
bool canApplyMarkdown, bool canApplyMarkdown,

View file

@ -811,6 +811,8 @@ Manager::DisplayOptions Manager::getNotificationOptions(
const auto hideEverything = Core::App().passcodeLocked() const auto hideEverything = Core::App().passcodeLocked()
|| forceHideDetails(); || forceHideDetails();
const auto view = Core::App().settings().notifyView(); const auto view = Core::App().settings().notifyView();
const auto peer = item ? item->history()->peer.get() : nullptr;
const auto topic = item ? item->topic() : nullptr;
auto result = DisplayOptions(); auto result = DisplayOptions();
result.hideNameAndPhoto = hideEverything result.hideNameAndPhoto = hideEverything
@ -820,12 +822,11 @@ Manager::DisplayOptions Manager::getNotificationOptions(
result.hideMarkAsRead = result.hideMessageText result.hideMarkAsRead = result.hideMessageText
|| (type != Data::ItemNotificationType::Message) || (type != Data::ItemNotificationType::Message)
|| !item || !item
|| ((item->out() || item->history()->peer->isSelf()) || ((item->out() || peer->isSelf()) && item->isFromScheduled());
&& item->isFromScheduled());
result.hideReplyButton = result.hideMarkAsRead result.hideReplyButton = result.hideMarkAsRead
|| !item->history()->peer->canWrite() || (!peer->canWrite() && (!topic || !topic->canWrite()))
|| item->history()->peer->isBroadcast() || peer->isBroadcast()
|| (item->history()->peer->slowmodeSecondsLeft() > 0); || (peer->slowmodeSecondsLeft() > 0);
return result; return result;
} }

View file

@ -1219,7 +1219,7 @@ void PeerMenuShareContactBox(
// There is no async to make weak from controller. // There is no async to make weak from controller.
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>(); const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto callback = [=](not_null<PeerData*> peer) { auto callback = [=](not_null<PeerData*> peer) {
if (!peer->canWrite()) { if (!peer->canWrite()) { // #TODO forum forward
navigation->parentController()->show( navigation->parentController()->show(
Ui::MakeInformBox(tr::lng_forward_share_cant()), Ui::MakeInformBox(tr::lng_forward_share_cant()),
Ui::LayerOption::KeepOther); Ui::LayerOption::KeepOther);
@ -1529,10 +1529,10 @@ QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
? tr::lng_scheduled_send_now_many(tr::now, lt_count, items.size()) ? tr::lng_scheduled_send_now_many(tr::now, lt_count, items.size())
: tr::lng_scheduled_send_now(tr::now); : tr::lng_scheduled_send_now(tr::now);
const auto list = session->data().idsToItems(items);
const auto error = GetErrorTextForSending( const auto error = GetErrorTextForSending(
history->peer, history->peer,
session->data().idsToItems(items), { .forward = &list });
TextWithTags());
if (!error.isEmpty()) { if (!error.isEmpty()) {
Ui::ShowMultilineToast({ Ui::ShowMultilineToast({
.parentOverride = Window::Show(navigation).toastParent(), .parentOverride = Window::Show(navigation).toastParent(),

@ -1 +1 @@
Subproject commit 4ba3000a288772752fcf9b41a618ce5df5a185a5 Subproject commit c199a1722fae72e254753f3095444a3c82a2a704