diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 08e541bec..0ab97dc5e 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -3015,6 +3015,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_rights_chat_send_links" = "Embed links"; "lng_rights_chat_send_polls" = "Send polls"; "lng_rights_chat_add_members" = "Add members"; +"lng_rights_chat_photos" = "Photos"; +"lng_rights_chat_videos" = "Video files"; +"lng_rights_chat_stickers" = "Stickers & GIFs"; +"lng_rights_chat_music" = "Music"; +"lng_rights_chat_files" = "Files"; +"lng_rights_chat_voice_messages" = "Voice messages"; +"lng_rights_chat_video_messages" = "Video messages"; "lng_rights_chat_banned_until_header" = "Restricted until"; "lng_rights_chat_banned_forever" = "Forever"; "lng_rights_chat_banned_day#one" = "For {count} day"; @@ -3044,21 +3051,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bots_password_confirm_description" = "Please enter your password to confirm the action."; "lng_restricted_send_message" = "The admins of this group restricted you from writing here."; -"lng_restricted_send_media" = "The admins of this group restricted you from posting media content here."; +"lng_restricted_send_photos" = "The admins of this group restricted you from posting photos here."; +"lng_restricted_send_videos" = "The admins of this group restricted you from posting video files here."; +"lng_restricted_send_music" = "The admins of this group restricted you from posting music here."; +"lng_restricted_send_files" = "The admins of this group restricted you from posting files here."; +"lng_restricted_send_voice_messages_group" = "The admins of this group restricted you from posting voice messages here."; +"lng_restricted_send_video_messages_group" = "The admins of this group restricted you from posting video messages here."; "lng_restricted_send_stickers" = "The admins of this group restricted you from posting stickers here."; "lng_restricted_send_gifs" = "The admins of this group restricted you from posting GIFs here."; "lng_restricted_send_inline" = "The admins of this group restricted you from posting inline content here."; "lng_restricted_send_polls" = "The admins of this group restricted you from posting polls here."; "lng_restricted_send_message_until" = "The admins of this group restricted you from writing here until {date}, {time}."; -"lng_restricted_send_media_until" = "The admins of this group restricted you from posting media content here until {date}, {time}."; +"lng_restricted_send_photos_until" = "The admins of this group restricted you from posting photos here until {date}, {time}."; +"lng_restricted_send_videos_until" = "The admins of this group restricted you from posting video files here until {date}, {time}."; +"lng_restricted_send_music_until" = "The admins of this group restricted you from posting music here until {date}, {time}."; +"lng_restricted_send_files_until" = "The admins of this group restricted you from posting files here until {date}, {time}."; +"lng_restricted_send_voice_messages_until" = "The admins of this group restricted you from posting voice messages here until {date}, {time}."; +"lng_restricted_send_video_messages_until" = "The admins of this group restricted you from posting video messages here until {date}, {time}."; "lng_restricted_send_stickers_until" = "The admins of this group restricted you from posting stickers here until {date}, {time}."; "lng_restricted_send_gifs_until" = "The admins of this group restricted you from posting GIFs here until {date}, {time}."; "lng_restricted_send_inline_until" = "The admins of this group restricted you from posting inline content here until {date}, {time}."; "lng_restricted_send_polls_until" = "The admins of this group restricted you from posting polls here until {date}, {time}."; "lng_restricted_send_message_all" = "Writing messages isn't allowed in this group."; -"lng_restricted_send_media_all" = "Posting media content isn't allowed in this group."; +"lng_restricted_send_photos_all" = "Posting photos isn't allowed in this group."; +"lng_restricted_send_videos_all" = "Posting video files isn't allowed in this group."; +"lng_restricted_send_music_all" = "Posting music isn't allowed in this group."; +"lng_restricted_send_files_all" = "Posting files isn't allowed in this group."; +"lng_restricted_send_voice_messages_all" = "Posting voice messages isn't allowed in this group."; +"lng_restricted_send_video_messages_all" = "Posting video messages isn't allowed in this group."; "lng_restricted_send_stickers_all" = "Posting stickers isn't allowed in this group."; "lng_restricted_send_gifs_all" = "Posting GIFs isn't allowed in this group."; "lng_restricted_send_inline_all" = "Posting inline content isn't allowed in this group."; @@ -3219,7 +3241,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_admin_log_restricted_until" = "until {date}"; "lng_admin_log_banned_view_messages" = "Read messages"; "lng_admin_log_banned_send_messages" = "Send messages"; -"lng_admin_log_banned_send_media" = "Send media"; +"lng_admin_log_banned_send_photos" = "Send photos"; +"lng_admin_log_banned_send_videos" = "Send video files"; +"lng_admin_log_banned_send_music" = "Send music"; +"lng_admin_log_banned_send_files" = "Send files"; +"lng_admin_log_banned_send_voice_messages" = "Send voice messages"; +"lng_admin_log_banned_send_video_messages" = "Send video messages"; "lng_admin_log_banned_send_stickers" = "Send stickers & GIFs"; "lng_admin_log_banned_embed_links" = "Embed links"; "lng_admin_log_banned_send_polls" = "Send polls"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 7e75c8693..5dea3d898 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3509,7 +3509,7 @@ void ApiWrap::sendMessage(MessageToSend &&message) { ? action.topicRootId : Data::ForumTopic::kGeneralId; const auto topic = peer->forumTopicFor(topicRootId); - if (!(topic ? topic->canWrite() : peer->canWrite()) + if (!(topic ? Data::CanSendTexts(topic) : Data::CanSendTexts(peer)) || Api::SendDice(message)) { return; } diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 8661b58d4..0679d4dc9 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -534,9 +534,8 @@ auto ChooseRecipientBoxController::createRow( const auto peer = history->peer; const auto skip = _filter ? !_filter(history) - : ((peer->isBroadcast() && !peer->canWrite()) - || (peer->isUser() && !peer->canWrite()) - || peer->isRepliesChat()); + : ((peer->isBroadcast() && !Data::CanSendAnything(peer)) + || (peer->isUser() && !Data::CanSendAnything(peer))); return skip ? nullptr : std::make_unique(history); } @@ -752,6 +751,6 @@ std::unique_ptr ChooseTopicBoxController::createSearchRow( auto ChooseTopicBoxController::createRow(not_null topic) -> std::unique_ptr { - const auto skip = _filter ? !_filter(topic) : !topic->canWrite(); + const auto skip = _filter && !_filter(topic); return skip ? nullptr : std::make_unique(topic); }; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index 7ff36d3ba..416c4ecb7 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -1200,11 +1200,14 @@ object_ptr ShareInviteLinkBox( (*box)->closeBox(); } }; + auto filterCallback = [](not_null thread) { + return Data::CanSendTexts(thread); + }; auto object = Box(ShareBox::Descriptor{ .session = &peer->session(), .copyCallback = std::move(copyCallback), .submitCallback = std::move(submitCallback), - .filterCallback = [](auto thread) { return thread->canWrite(); }, + .filterCallback = std::move(filterCallback), }); *box = Ui::MakeWeak(object.data()); return object; diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp index 376cd7c1a..28e3f043f 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp @@ -123,23 +123,26 @@ auto Dependencies(ChatRestrictions) { Flag::SendInline, Flag::SendStickers }, { Flag::SendStickers, Flag::SendInline }, - // stickers -> send_messages - { Flag::SendStickers, Flag::SendMessages }, + // embed_links -> send_plain + { Flag::EmbedLinks, Flag::SendOther }, - // embed_links -> send_messages - { Flag::EmbedLinks, Flag::SendMessages }, - - // send_media -> send_messages - { Flag::SendMedia, Flag::SendMessages }, - - // send_polls -> send_messages - { Flag::SendPolls, Flag::SendMessages }, - - // send_messages -> view_messages - { Flag::SendMessages, Flag::ViewMessages }, + // send_* -> view_messages + { Flag::SendStickers, Flag::ViewMessages }, + { Flag::SendGifs, Flag::ViewMessages }, + { Flag::SendGames, Flag::ViewMessages }, + { Flag::SendInline, Flag::ViewMessages }, + { Flag::SendPolls, Flag::ViewMessages }, + { Flag::SendPhotos, Flag::ViewMessages }, + { Flag::SendVideos, Flag::ViewMessages }, + { Flag::SendVideoMessages, Flag::ViewMessages }, + { Flag::SendMusic, Flag::ViewMessages }, + { Flag::SendVoiceMessages, Flag::ViewMessages }, + { Flag::SendFiles, Flag::ViewMessages }, + { Flag::SendOther, Flag::ViewMessages }, }; } + ChatRestrictions NegateRestrictions(ChatRestrictions value) { using Flag = ChatRestriction; @@ -154,10 +157,15 @@ ChatRestrictions NegateRestrictions(ChatRestrictions value) { | Flag::SendGames | Flag::SendGifs | Flag::SendInline - | Flag::SendMedia - | Flag::SendMessages | Flag::SendPolls - | Flag::SendStickers); + | Flag::SendStickers + | Flag::SendPhotos + | Flag::SendVideos + | Flag::SendVideoMessages + | Flag::SendMusic + | Flag::SendVoiceMessages + | Flag::SendFiles + | Flag::SendOther); } auto Dependencies(ChatAdminRights) @@ -722,13 +730,20 @@ void EditPeerPermissionsBox::addBannedButtons( std::vector RestrictionLabels( Data::RestrictionsSetOptions options) { using Flag = ChatRestriction; + auto result = std::vector{ - { Flag::SendMessages, tr::lng_rights_chat_send_text(tr::now) }, - { Flag::SendMedia, tr::lng_rights_chat_send_media(tr::now) }, + { Flag::SendOther, tr::lng_rights_chat_send_text(tr::now) }, + // { Flag::SendMedia, tr::lng_rights_chat_send_media(tr::now) }, + { Flag::SendPhotos, tr::lng_rights_chat_photos(tr::now) }, + { Flag::SendVideos, tr::lng_rights_chat_videos(tr::now) }, + { Flag::SendVideoMessages, tr::lng_rights_chat_video_messages(tr::now) }, + { Flag::SendMusic, tr::lng_rights_chat_music(tr::now) }, + { Flag::SendVoiceMessages, tr::lng_rights_chat_voice_messages(tr::now) }, + { Flag::SendFiles, tr::lng_rights_chat_files(tr::now) }, { Flag::SendStickers | Flag::SendGifs | Flag::SendGames - | Flag::SendInline, tr::lng_rights_chat_send_stickers(tr::now) }, + | Flag::SendInline, tr::lng_rights_chat_stickers(tr::now) }, { Flag::EmbedLinks, tr::lng_rights_chat_send_links(tr::now) }, { Flag::SendPolls, tr::lng_rights_chat_send_polls(tr::now) }, { Flag::AddParticipants, tr::lng_rights_chat_add_members(tr::now) }, diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 3a205d77a..df1526780 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -114,6 +114,41 @@ rpl::producer FieldPlaceholder( } // namespace +SendFilesLimits DefaultLimitsForPeer(not_null peer) { + using Flag = SendFilesAllow; + using Restriction = ChatRestriction; + const auto allowByRestriction = [&](Restriction check, Flag allow) { + return Data::RestrictionError(peer, check) ? Flag() : allow; + }; + return Flag() + | (peer->slowmodeApplied() ? Flag::OnlyOne : Flag()) + | (Data::AllowEmojiWithoutPremium(peer) + ? Flag::EmojiWithoutPremium + : Flag()) + | allowByRestriction(Restriction::SendPhotos, Flag::Photos) + | allowByRestriction(Restriction::SendVideos, Flag::Videos) + | allowByRestriction(Restriction::SendMusic, Flag::Music) + | allowByRestriction(Restriction::SendFiles, Flag::Files) + | allowByRestriction(Restriction::SendStickers, Flag::Stickers) + | allowByRestriction(Restriction::SendGifs, Flag::Gifs) + | allowByRestriction(Restriction::SendOther, Flag::Texts); +} + +SendFilesCheck DefaultCheckForPeer( + not_null controller, + not_null peer) { + return [=]( + const Ui::PreparedFile &file, + bool compress, + bool silent) { + const auto error = Data::FileRestrictionError(peer, file, compress); + if (error && !silent) { + controller->showToast({ *error }); + } + return !error.has_value(); + }; +} + SendFilesBox::Block::Block( not_null parent, not_null*> items, @@ -289,16 +324,17 @@ SendFilesBox::SendFilesBox( not_null controller, Ui::PreparedList &&list, const TextWithTags &caption, - not_null peer, + SendFilesLimits limits, + SendFilesCheck check, Api::SendType sendType, SendMenu::Type sendMenuType) : _controller(controller) , _sendType(sendType) , _titleHeight(st::boxTitleHeight) , _list(std::move(list)) -, _sendLimit(peer->slowmodeApplied() ? SendLimit::One : SendLimit::Many) +, _limits(limits) , _sendMenuType(sendMenuType) -, _allowEmojiWithoutPremium(Data::AllowEmojiWithoutPremium(peer)) +, _check(std::move(check)) , _caption(this, st::confirmCaptionArea, Ui::InputField::Mode::MultiLine) , _prefilledCaptionText(std::move(caption)) , _scroll(this, st::boxScroll) @@ -419,6 +455,11 @@ void SendFilesBox::refreshAllAfterChanges(int fromItem, Fn perform) { { auto sendWay = _sendWay.current(); sendWay.setHasCompressedStickers(_list.hasSticker()); + if (_limits & SendFilesAllow::OnlyOne) { + if (_list.files.size() > 1) { + sendWay.setGroupFiles(true); + } + } _sendWay = sendWay; } _inner->resizeToWidth(st::boxWideWidth); @@ -429,7 +470,8 @@ void SendFilesBox::refreshAllAfterChanges(int fromItem, Fn perform) { void SendFilesBox::openDialogToAddFileToAlbum() { const auto toastParent = Ui::BoxShow(this).toastParent(); const auto checkResult = [=](const Ui::PreparedList &list) { - if (_sendLimit != SendLimit::One) { + if (_check) + if (!(_limits & SendFilesAllow::OnlyOne)) { return true; } else if (!_list.canBeSentInSlowmodeWith(list)) { Ui::Toast::Show(toastParent, tr::lng_slowmode_no_many(tr::now)); @@ -555,20 +597,36 @@ void SendFilesBox::initSendWay() { _sendWay = [&] { auto result = Core::App().settings().sendFilesWay(); result.setHasCompressedStickers(_list.hasSticker()); - if (_sendLimit == SendLimit::One) { + if ((_limits & SendFilesAllow::OnlyOne) + && (_list.files.size() > 1)) { result.setGroupFiles(true); - return result; - } else if (_list.overrideSendImagesAsPhotos == false) { - result.setSendImagesAsPhotos(false); + } + if (_list.overrideSendImagesAsPhotos == false) { + if (!(_limits & SendFilesAllow::OnlyOne) + || !_list.hasSticker()) { + result.setSendImagesAsPhotos(false); + } return result; } else if (_list.overrideSendImagesAsPhotos == true) { result.setSendImagesAsPhotos(true); + const auto silent = true; + if (!checkWithWay(result, silent)) { + result.setSendImagesAsPhotos(false); + } return result; } + const auto silent = true; + if (!checkWithWay(result, silent)) { + result.setSendImagesAsPhotos(!result.sendImagesAsPhotos()); + } return result; }(); _sendWay.changes( ) | rpl::start_with_next([=](SendFilesWay value) { + const auto hidden = [&] { + return !_caption || _caption->isHidden(); + }; + const auto was = hidden(); updateCaptionPlaceholder(); updateEmojiPanelGeometry(); for (auto &block : _blocks) { @@ -577,6 +635,10 @@ void SendFilesBox::initSendWay() { if (!hasSendMenu()) { refreshButtons(); } + if (was != hidden()) { + updateBoxSize(); + updateControlsGeometry(); + } setInnerFocus(); }, lifetime()); } @@ -589,7 +651,8 @@ void SendFilesBox::updateCaptionPlaceholder() { if (!_list.canAddCaption( way.groupFiles() && way.sendImagesAsPhotos(), way.sendImagesAsPhotos()) - && _sendLimit == SendLimit::One) { + && ((_limits & SendFilesAllow::OnlyOne) + || !(_limits & SendFilesAllow::Texts))) { _caption->hide(); if (_emojiToggle) { _emojiToggle->hide(); @@ -695,8 +758,8 @@ void SendFilesBox::pushBlock(int from, int till) { _list.files[index] = std::move(list.files.front()); }); }; - const auto checkResult = [=](const Ui::PreparedList &list) { - if (_sendLimit != SendLimit::One) { + const auto checkSlowmode = [=](const Ui::PreparedList &list) { + if (list.files.empty() || !(_limits & SendFilesAllow::OnlyOne)) { return true; } auto removing = std::move(_list.files[index]); @@ -713,6 +776,37 @@ void SendFilesBox::pushBlock(int from, int till) { } return true; }; + const auto checkRights = [=](const Ui::PreparedList &list) { + if (list.files.empty()) { + return true; + } + auto removing = std::move(_list.files[index]); + std::swap(_list.files[index], _list.files.back()); + _list.files.pop_back(); + auto way = _sendWay.current(); + const auto has = _list.hasSticker() + || list.files.front().isSticker(); + way.setHasCompressedStickers(has); + if (_limits & SendFilesAllow::OnlyOne) { + way.setGroupFiles(true); + } + const auto silent = true; + if (!checkWith(list, way, silent) + && (!(_limits & SendFilesAllow::OnlyOne) || !has)) { + way.setSendImagesAsPhotos(!way.sendImagesAsPhotos()); + } + const auto result = checkWith(list, way); + _list.files.push_back(std::move(removing)); + std::swap(_list.files[index], _list.files.back()); + if (!result) { + return false; + } + _sendWay = way; + return true; + }; + const auto checkResult = [=](const Ui::PreparedList &list) { + return checkSlowmode(list) && checkRights(list); + }; const auto callback = [=](FileDialog::OpenResult &&result) { const auto premium = _controller->session().premium(); FileDialogCallback( @@ -767,7 +861,7 @@ void SendFilesBox::setupSendWayControls() { _sendImagesAsPhotos.create( this, tr::lng_send_compressed(tr::now), - asPhotosFirst, + _sendWay.current().sendImagesAsPhotos(), st::defaultBoxCheckbox); _sendWay.changes( @@ -777,17 +871,35 @@ void SendFilesBox::setupSendWayControls() { }, lifetime()); _groupFiles->checkedChanges( - ) | rpl::start_with_next([=] { + ) | rpl::start_with_next([=](bool checked) { auto sendWay = _sendWay.current(); - sendWay.setGroupFiles(_groupFiles->checked()); - _sendWay = sendWay; + if (sendWay.groupFiles() == checked) { + return; + } + sendWay.setGroupFiles(checked); + if (checkWithWay(sendWay)) { + _sendWay = sendWay; + } else { + Ui::PostponeCall(_groupFiles.data(), [=] { + _groupFiles->setChecked(!checked); + }); + } }, lifetime()); _sendImagesAsPhotos->checkedChanges( - ) | rpl::start_with_next([=] { + ) | rpl::start_with_next([=](bool checked) { auto sendWay = _sendWay.current(); - sendWay.setSendImagesAsPhotos(_sendImagesAsPhotos->checked()); - _sendWay = sendWay; + if (sendWay.sendImagesAsPhotos() == checked) { + return; + } + sendWay.setSendImagesAsPhotos(checked); + if (checkWithWay(sendWay)) { + _sendWay = sendWay; + } else { + Ui::PostponeCall(_sendImagesAsPhotos.data(), [=] { + _sendImagesAsPhotos->setChecked(!checked); + }); + } }, lifetime()); _wayRemember.create( @@ -811,8 +923,29 @@ void SendFilesBox::setupSendWayControls() { st::editMediaHintLabel); } +bool SendFilesBox::checkWithWay(Ui::SendFilesWay way, bool silent) const { + return checkWith({}, way, silent); +} + +bool SendFilesBox::checkWith( + const Ui::PreparedList &added, + Ui::SendFilesWay way, + bool silent) const { + if (!_check) { + return true; + } + const auto compress = way.sendImagesAsPhotos(); + auto &already = _list.files; + for (const auto &file : ranges::views::concat(already, added.files)) { + if (!_check(file, compress, silent)) { + return false; + } + } + return true; +} + void SendFilesBox::updateSendWayControls() { - const auto onlyOne = (_sendLimit == SendLimit::One); + const auto onlyOne = (_limits & SendFilesAllow::OnlyOne); _groupFiles->setVisible(_list.hasGroupOption(onlyOne)); _sendImagesAsPhotos->setVisible( _list.hasSendImagesAsPhotosOption(onlyOne)); @@ -828,7 +961,7 @@ void SendFilesBox::updateSendWayControls() { void SendFilesBox::setupCaption() { const auto allow = [=](const auto&) { - return _allowEmojiWithoutPremium; + return (_limits & SendFilesAllow::EmojiWithoutPremium); }; InitMessageFieldHandlers( _controller, @@ -899,7 +1032,7 @@ void SendFilesBox::setupEmojiPanel() { st::emojiPanMinHeight); _emojiPanel->hide(); _emojiPanel->selector()->setAllowEmojiWithoutPremium( - _allowEmojiWithoutPremium); + _limits & SendFilesAllow::EmojiWithoutPremium); _emojiPanel->selector()->emojiChosen( ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) { Ui::InsertEmojiAtCursor(_caption->textCursor(), data.emoji); @@ -910,7 +1043,7 @@ void SendFilesBox::setupEmojiPanel() { if (info && info->setType == Data::StickersType::Emoji && !_controller->session().premium() - && !_allowEmojiWithoutPremium) { + && !(_limits & SendFilesAllow::EmojiWithoutPremium)) { ShowPremiumPreviewBox( _controller, PremiumPreview::AnimatedEmoji); @@ -1026,7 +1159,20 @@ void SendFilesBox::addFile(Ui::PreparedFile &&file) { // canBeSentInSlowmode checks for non empty filesToProcess. auto saved = base::take(_list.filesToProcess); _list.files.push_back(std::move(file)); - if (_sendLimit == SendLimit::One && !_list.canBeSentInSlowmode()) { + const auto lastOk = [&] { + auto way = _sendWay.current(); + if (_limits & SendFilesAllow::OnlyOne) { + way.setGroupFiles(true); + if (!_list.canBeSentInSlowmode()) { + return false; + } + } else if (!checkWithWay(way)) { + return false; + } + _sendWay = way; + return true; + }(); + if (!lastOk) { _list.files.pop_back(); } _list.filesToProcess = std::move(saved); @@ -1058,7 +1204,7 @@ void SendFilesBox::refreshTitleText() { void SendFilesBox::updateBoxSize() { auto footerHeight = 0; - if (_caption) { + if (_caption && !_caption->isHidden()) { footerHeight += st::boxPhotoCaptionSkip + _caption->height(); } const auto pairs = std::array, 4>{ { @@ -1113,7 +1259,7 @@ void SendFilesBox::resizeEvent(QResizeEvent *e) { void SendFilesBox::updateControlsGeometry() { auto bottom = height(); - if (_caption) { + if (_caption && !_caption->isHidden()) { _caption->resize(st::sendMediaPreviewSize, _caption->height()); _caption->moveToLeft( st::boxPhotoPadding.left(), diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 63e05fe47..22510b9f3 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include +#include "base/flags.h" #include "boxes/abstract_box.h" #include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_send_files_way.h" @@ -48,6 +48,30 @@ namespace SendMenu { enum class Type; } // namespace SendMenu +enum class SendFilesAllow { + OnlyOne = (1 << 0), + Photos = (1 << 1), + Videos = (1 << 2), + Music = (1 << 3), + Files = (1 << 4), + Stickers = (1 << 5), + Gifs = (1 << 6), + EmojiWithoutPremium = (1 << 7), + Texts = (1 << 8), +}; +inline constexpr bool is_flag_type(SendFilesAllow) { return true; } +using SendFilesLimits = base::flags; + +using SendFilesCheck = Fn; + +[[nodiscard]] SendFilesLimits DefaultLimitsForPeer(not_null peer); +[[nodiscard]] SendFilesCheck DefaultCheckForPeer( + not_null controller, + not_null peer); + class SendFilesBox : public Ui::BoxContent { public: enum class SendLimit { @@ -59,7 +83,8 @@ public: not_null controller, Ui::PreparedList &&list, const TextWithTags &caption, - not_null peer, + SendFilesLimits limits, + SendFilesCheck check, Api::SendType sendType, SendMenu::Type sendMenuType); @@ -126,6 +151,13 @@ private: [[nodiscard]] bool hasSendMenu() const; [[nodiscard]] bool hasSpoilerMenu() const; [[nodiscard]] bool allWithSpoilers(); + [[nodiscard]] bool checkWithWay( + Ui::SendFilesWay way, + bool silent = false) const; + [[nodiscard]] bool checkWith( + const Ui::PreparedList &added, + Ui::SendFilesWay way, + bool silent = false) const; void addMenuButton(); void applyBlockChanges(); void toggleSpoilers(bool enabled); @@ -177,10 +209,10 @@ private: Ui::PreparedList _list; std::optional _removingIndex; - SendLimit _sendLimit = SendLimit::Many; + SendFilesLimits _limits = {}; SendMenu::Type _sendMenuType = SendMenu::Type(); - bool _allowEmojiWithoutPremium = false; + SendFilesCheck _check; Fn forum) { box->closeBox(); }, box->lifetime()); }; + auto filter = [=](not_null topic) { + return guard && _descriptor.filterCallback(topic); + }; auto box = Box( std::make_unique( forum, - std::move(chosen)), + std::move(chosen), + std::move(filter)), std::move(initBox)); *weak = box.data(); _show->showBox(std::move(box)); @@ -1507,8 +1511,12 @@ void FastShareMessage( } }; - auto filterCallback = [isGame](not_null thread) { - return thread->canWrite() + const auto requiredRight = item->requiredSendRight(); + const auto requiresInline = item->requiresSendInlineRight(); + auto filterCallback = [=](not_null thread) { + return Data::CanSend(thread, requiredRight) + && (!requiresInline + || Data::CanSend(thread, ChatRestriction::SendInline)) && (!isGame || !thread->peer()->isBroadcast()); }; auto copyLinkCallback = canCopyLink diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index be53c2aca..fcec2a8ff 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1639,7 +1639,7 @@ void Members::setupAddMember(not_null call) { return rpl::single(false) | rpl::type_erased(); } return rpl::combine( - Data::CanWriteValue(peer, false), + Data::CanSendValue(peer, ChatRestriction::SendOther, false), _call->joinAsValue() ) | rpl::map([=](bool can, not_null joinAs) { return can && joinAs->isSelf(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 6291f6667..0a6dd7790 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -845,7 +845,7 @@ void Panel::setupMembers() { _members->addMembersRequests( ) | rpl::start_with_next([=] { if (!_peer->isBroadcast() - && _peer->canWrite(false) + && Data::CanSend(_peer, ChatRestriction::SendOther, false) && _call->joinAs()->isSelf()) { addMembers(); } else if (const auto channel = _peer->asChannel()) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp index 210427142..831fcd3c2 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp @@ -194,7 +194,7 @@ object_ptr ShareInviteLinkBox( showToast(tr::lng_share_done(tr::now)); }; auto filterCallback = [](not_null thread) { - return thread->canWrite(); + return Data::CanSend(thread, ChatRestriction::SendOther); }; const auto scheduleStyle = [&] { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 8110dc962..8da279f6c 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -388,7 +388,9 @@ TabbedSelector::TabbedSelector( _tabsSlider->raise(); } - if (hasStickersTab() || hasGifsTab()) { + if (hasStickersTab() + || hasGifsTab() + || (hasEmojiTab() && _mode == Mode::Full)) { session().changes().peerUpdates( Data::PeerUpdate::Flag::Rights ) | rpl::filter([=](const Data::PeerUpdate &update) { @@ -892,6 +894,14 @@ void TabbedSelector::checkRestrictedPeer() { ? Data::RestrictionError( _currentPeer, ChatRestriction::SendGifs) + : (_currentTabType == SelectorTab::Emoji && _mode == Mode::Full) + ? (Data::RestrictionError( + _currentPeer, + ChatRestriction::SendInline) + ? Data::RestrictionError( + _currentPeer, + ChatRestriction::SendOther) + : std::nullopt) : std::nullopt; if (error) { if (!_restrictedLabel) { diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 5152b569e..d8ffae90e 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -321,13 +321,18 @@ ChatRestrictionsInfo ChannelData::KickedRestrictedRights( not_null participant) { using Flag = ChatRestriction; const auto flags = Flag::ViewMessages - | Flag::SendMessages - | Flag::SendMedia - | Flag::EmbedLinks | Flag::SendStickers | Flag::SendGifs | Flag::SendGames - | Flag::SendInline; + | Flag::SendInline + | Flag::SendPhotos + | Flag::SendVideos + | Flag::SendVideoMessages + | Flag::SendMusic + | Flag::SendVoiceMessages + | Flag::SendFiles + | Flag::SendOther + | Flag::EmbedLinks; return ChatRestrictionsInfo( (participant->isUser() ? flags : Flag::ViewMessages), std::numeric_limits::max()); @@ -549,10 +554,6 @@ bool ChannelData::canAddMembers() const { : ((adminRights() & AdminRight::InviteByLinkOrAdd) || amCreator()); } -bool ChannelData::canSendPolls() const { - return canWrite() && !amRestricted(ChatRestriction::SendPolls); -} - bool ChannelData::canAddAdmins() const { return amCreator() || (adminRights() & AdminRight::AddAdmins); @@ -563,18 +564,6 @@ bool ChannelData::canPublish() const { || (adminRights() & AdminRight::PostMessages); } -bool ChannelData::canWrite(bool checkForForum) const { - // Duplicated in Data::CanWriteValue(). - const auto allowed = amIn() - || ((flags() & Flag::HasLink) && !(flags() & Flag::JoinToWrite)); - const auto forumRestriction = checkForForum && isForum(); - return allowed - && !forumRestriction - && (canPublish() - || (!isBroadcast() - && !amRestricted(Restriction::SendMessages))); -} - bool ChannelData::allowsForwarding() const { return !(flags() & Flag::NoForwards); } diff --git a/Telegram/SourceFiles/data/data_channel.h b/Telegram/SourceFiles/data/data_channel.h index 7c928ad8c..9817da915 100644 --- a/Telegram/SourceFiles/data/data_channel.h +++ b/Telegram/SourceFiles/data/data_channel.h @@ -318,7 +318,6 @@ public: void setDefaultRestrictions(ChatRestrictions rights); // Like in ChatData. - [[nodiscard]] bool canWrite(bool checkForForum = true) const; [[nodiscard]] bool allowsForwarding() const; [[nodiscard]] bool canEditInformation() const; [[nodiscard]] bool canEditPermissions() const; @@ -327,7 +326,6 @@ public: [[nodiscard]] bool canAddMembers() const; [[nodiscard]] bool canAddAdmins() const; [[nodiscard]] bool canBanMembers() const; - [[nodiscard]] bool canSendPolls() const; [[nodiscard]] bool anyoneCanAddMembers() const; [[nodiscard]] bool canEditMessages() const; diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 0315c0aed..fa6fe3c7c 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -63,11 +63,6 @@ ChatAdminRightsInfo ChatData::defaultAdminRights(not_null user) { | (isCreator ? Flag::AddAdmins : Flag(0))); } -bool ChatData::canWrite() const { - // Duplicated in Data::CanWriteValue(). - return amIn() && !amRestricted(ChatRestriction::SendMessages); -} - bool ChatData::allowsForwarding() const { return !(flags() & Flag::NoForwards); } @@ -99,10 +94,6 @@ bool ChatData::canAddMembers() const { return amIn() && !amRestricted(ChatRestriction::AddParticipants); } -bool ChatData::canSendPolls() const { - return amIn() && !amRestricted(ChatRestriction::SendPolls); -} - bool ChatData::canAddAdmins() const { return amIn() && amCreator(); } diff --git a/Telegram/SourceFiles/data/data_chat.h b/Telegram/SourceFiles/data/data_chat.h index eaccda8c4..93202608a 100644 --- a/Telegram/SourceFiles/data/data_chat.h +++ b/Telegram/SourceFiles/data/data_chat.h @@ -100,7 +100,6 @@ public: not_null user); // Like in ChannelData. - [[nodiscard]] bool canWrite() const; [[nodiscard]] bool allowsForwarding() const; [[nodiscard]] bool canEditInformation() const; [[nodiscard]] bool canEditPermissions() const; @@ -110,7 +109,6 @@ public: [[nodiscard]] bool canAddMembers() const; [[nodiscard]] bool canAddAdmins() const; [[nodiscard]] bool canBanMembers() const; - [[nodiscard]] bool canSendPolls() const; [[nodiscard]] bool anyoneCanAddMembers() const; void applyEditAdmin(not_null user, bool isAdmin); diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp index 5aad9b2d9..0a4922989 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.cpp +++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp @@ -7,7 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/data_chat_participant_status.h" +#include "base/unixtime.h" #include "boxes/peers/edit_peer_permissions_box.h" +#include "data/data_chat.h" +#include "data/data_channel.h" +#include "data/data_forum_topic.h" +#include "data/data_peer_values.h" +#include "data/data_user.h" +#include "lang/lang_keys.h" +#include "ui/chat/attach/attach_prepare.h" namespace { @@ -53,4 +61,313 @@ std::vector ListOfRestrictions( | ranges::to_vector; } +ChatRestrictions AllSendRestrictions() { + constexpr auto result = [] { + auto result = ChatRestrictions(); + for (const auto right : AllSendRestrictionsList()) { + result |= right; + } + return result; + }(); + return result; +} + +ChatRestrictions FilesSendRestrictions() { + constexpr auto result = [] { + auto result = ChatRestrictions(); + for (const auto right : FilesSendRestrictionsList()) { + result |= right; + } + return result; + }(); + return result; +} + +ChatRestrictions TabbedPanelSendRestrictions() { + constexpr auto result = [] { + auto result = ChatRestrictions(); + for (const auto right : TabbedPanelSendRestrictionsList()) { + result |= right; + } + return result; + }(); + return result; +} + +// Duplicated in CanSendAnyOfValue(). +bool CanSendAnyOf( + not_null thread, + ChatRestrictions rights, + bool forbidInForums) { + const auto peer = thread->peer(); + const auto topic = thread->asTopic(); + return CanSendAnyOf(peer, rights, forbidInForums && !topic) + && (!topic || !topic->closed() || topic->canToggleClosed()); +} + +// Duplicated in CanSendAnyOfValue(). +bool CanSendAnyOf( + not_null peer, + ChatRestrictions rights, + bool forbidInForums) { + if (const auto user = peer->asUser()) { + if (user->isInaccessible() || user->isRepliesChat()) { + return false; + } else if (rights + & ~(ChatRestriction::SendVoiceMessages + | ChatRestriction::SendVideoMessages + | ChatRestriction::SendPolls)) { + return true; + } + for (const auto right : { + ChatRestriction::SendVoiceMessages, + ChatRestriction::SendVideoMessages, + ChatRestriction::SendPolls, + }) { + if ((rights & right) && !user->amRestricted(right)) { + return true; + } + } + return false; + } else if (const auto chat = peer->asChat()) { + if (!chat->amIn()) { + return false; + } + for (const auto right : AllSendRestrictionsList()) { + if ((rights & right) && !chat->amRestricted(right)) { + return true; + } + } + return false; + } else if (const auto channel = peer->asChannel()) { + using Flag = ChannelDataFlag; + const auto allowed = channel->amIn() + || ((channel->flags() & Flag::HasLink) + && !(channel->flags() & Flag::JoinToWrite)); + if (!allowed || (forbidInForums && channel->isForum())) { + return false; + } else if (channel->canPublish()) { + return true; + } else if (channel->isBroadcast()) { + return false; + } + for (const auto right : AllSendRestrictionsList()) { + if ((rights & right) && !channel->amRestricted(right)) { + return true; + } + } + return false; + } + Unexpected("Peer type in CanSendAnyOf."); +} + +std::optional RestrictionError( + not_null peer, + ChatRestriction restriction) { + using Flag = ChatRestriction; + if (const auto restricted = peer->amRestricted(restriction)) { + if (const auto user = peer->asUser()) { + const auto result = (restriction == Flag::SendVoiceMessages) + ? tr::lng_restricted_send_voice_messages( + tr::now, + lt_user, + user->name()) + : (restriction == Flag::SendVoiceMessages) + ? tr::lng_restricted_send_video_messages( + tr::now, + lt_user, + user->name()) + : (restriction == Flag::SendPolls) + ? u"can't send polls :("_q + : (restriction == Flag::PinMessages) + ? u"can't pin :("_q + : std::optional(); + + Ensures(result.has_value()); + return result; + } + const auto all = restricted.isWithEveryone(); + const auto channel = peer->asChannel(); + if (!all && channel) { + auto restrictedUntil = channel->restrictedUntil(); + if (restrictedUntil > 0 + && !ChannelData::IsRestrictedForever(restrictedUntil)) { + auto restrictedUntilDateTime = base::unixtime::parse( + channel->restrictedUntil()); + auto date = QLocale().toString( + restrictedUntilDateTime.date(), + QLocale::ShortFormat); + auto time = QLocale().toString( + restrictedUntilDateTime.time(), + QLocale::ShortFormat); + + switch (restriction) { + case Flag::SendPolls: + return tr::lng_restricted_send_polls_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendOther: + return tr::lng_restricted_send_message_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendPhotos: + return tr::lng_restricted_send_photos_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendVideos: + return tr::lng_restricted_send_videos_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendMusic: + return tr::lng_restricted_send_music_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendFiles: + return tr::lng_restricted_send_files_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendVideoMessages: + return tr::lng_restricted_send_video_messages_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendVoiceMessages: + return tr::lng_restricted_send_voice_messages_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendStickers: + return tr::lng_restricted_send_stickers_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendGifs: + return tr::lng_restricted_send_gifs_until( + tr::now, lt_date, date, lt_time, time); + case Flag::SendInline: + case Flag::SendGames: + return tr::lng_restricted_send_inline_until( + tr::now, lt_date, date, lt_time, time); + } + Unexpected("Restriction in Data::RestrictionErrorKey."); + } + } + switch (restriction) { + case Flag::SendPolls: + return all + ? tr::lng_restricted_send_polls_all(tr::now) + : tr::lng_restricted_send_polls(tr::now); + case Flag::SendOther: + return all + ? tr::lng_restricted_send_message_all(tr::now) + : tr::lng_restricted_send_message(tr::now); + case Flag::SendPhotos: + return all + ? tr::lng_restricted_send_photos_all(tr::now) + : tr::lng_restricted_send_photos(tr::now); + case Flag::SendVideos: + return all + ? tr::lng_restricted_send_videos_all(tr::now) + : tr::lng_restricted_send_videos(tr::now); + case Flag::SendMusic: + return all + ? tr::lng_restricted_send_music_all(tr::now) + : tr::lng_restricted_send_music(tr::now); + case Flag::SendFiles: + return all + ? tr::lng_restricted_send_files_all(tr::now) + : tr::lng_restricted_send_files(tr::now); + case Flag::SendVideoMessages: + return all + ? tr::lng_restricted_send_video_messages_all(tr::now) + : tr::lng_restricted_send_video_messages_group(tr::now); + case Flag::SendVoiceMessages: + return all + ? tr::lng_restricted_send_voice_messages_all(tr::now) + : tr::lng_restricted_send_voice_messages_group(tr::now); + case Flag::SendStickers: + return all + ? tr::lng_restricted_send_stickers_all(tr::now) + : tr::lng_restricted_send_stickers(tr::now); + case Flag::SendGifs: + return all + ? tr::lng_restricted_send_gifs_all(tr::now) + : tr::lng_restricted_send_gifs(tr::now); + case Flag::SendInline: + case Flag::SendGames: + return all + ? tr::lng_restricted_send_inline_all(tr::now) + : tr::lng_restricted_send_inline(tr::now); + } + Unexpected("Restriction in Data::RestrictionErrorKey."); + } + return std::nullopt; +} + +std::optional AnyFileRestrictionError(not_null peer) { + using Restriction = ChatRestriction; + for (const auto right : FilesSendRestrictionsList()) { + if (!RestrictionError(peer, right)) { + return {}; + } + } + return RestrictionError(peer, Restriction::SendFiles); +} + +std::optional FileRestrictionError( + not_null peer, + const Ui::PreparedList &list, + std::optional compress) { + const auto slowmode = peer->slowmodeApplied(); + if (slowmode) { + if (!list.canBeSentInSlowmode()) { + return tr::lng_slowmode_no_many(tr::now); + } else if (list.files.size() > 1 && list.hasSticker()) { + if (compress == false) { + return tr::lng_slowmode_no_many(tr::now); + } else { + compress = true; + } + } + } + for (const auto &file : list.files) { + if (const auto error = FileRestrictionError(peer, file, compress)) { + return error; + } + } + return {}; +} + +std::optional FileRestrictionError( + not_null peer, + const Ui::PreparedFile &file, + std::optional compress) { + using Type = Ui::PreparedFile::Type; + using Restriction = ChatRestriction; + const auto stickers = RestrictionError(peer, Restriction::SendStickers); + const auto gifs = RestrictionError(peer, Restriction::SendGifs); + const auto photos = RestrictionError(peer, Restriction::SendPhotos); + const auto videos = RestrictionError(peer, Restriction::SendVideos); + const auto music = RestrictionError(peer, Restriction::SendMusic); + const auto files = RestrictionError(peer, Restriction::SendFiles); + if (!stickers && !gifs && !photos && !videos && !music && !files) { + return {}; + } + switch (file.type) { + case Type::Photo: + if (compress == true && photos) { + return photos; + } else if (const auto other = file.isSticker() ? stickers : files) { + if ((compress == false || photos) && other) { + return (file.isSticker() || !photos) ? other : photos; + } + } + break; + case Type::Video: + if (const auto error = file.isGifv() ? gifs : videos) { + return error; + } + break; + case Type::Music: + if (music) { + return music; + } + break; + case Type::File: + if (files) { + return files; + } + break; + } + return {}; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.h b/Telegram/SourceFiles/data/data_chat_participant_status.h index 828a742db..30dba03bf 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.h +++ b/Telegram/SourceFiles/data/data_chat_participant_status.h @@ -7,10 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -enum class UserRestriction { - SendVoiceMessages, - SendVideoMessages, -}; +namespace Ui { +struct PreparedList; +struct PreparedFile; +} // namespace Ui enum class ChatAdminRight { ChangeInfo = (1 << 0), @@ -31,14 +31,22 @@ using ChatAdminRights = base::flags; enum class ChatRestriction { ViewMessages = (1 << 0), - SendMessages = (1 << 1), - SendMedia = (1 << 2), + SendStickers = (1 << 3), SendGifs = (1 << 4), SendGames = (1 << 5), SendInline = (1 << 6), - EmbedLinks = (1 << 7), SendPolls = (1 << 8), + SendPhotos = (1 << 19), + SendVideos = (1 << 20), + SendVideoMessages = (1 << 21), + SendMusic = (1 << 22), + SendVoiceMessages = (1 << 23), + SendFiles = (1 << 24), + SendOther = (1 << 25), + + EmbedLinks = (1 << 7), + ChangeInfo = (1 << 10), AddParticipants = (1 << 15), PinMessages = (1 << 17), @@ -70,6 +78,8 @@ struct ChatRestrictionsInfo { namespace Data { +class Thread; + struct AdminRightsSetOptions { bool isGroup : 1 = false; bool isForum : 1 = false; @@ -83,4 +93,97 @@ struct RestrictionsSetOptions { [[nodiscard]] std::vector ListOfRestrictions( RestrictionsSetOptions options); +[[nodiscard]] inline constexpr auto AllSendRestrictionsList() { + return std::array{ + ChatRestriction::SendOther, + ChatRestriction::SendStickers, + ChatRestriction::SendGifs, + ChatRestriction::SendGames, + ChatRestriction::SendInline, + ChatRestriction::SendPolls, + ChatRestriction::SendPhotos, + ChatRestriction::SendVideos, + ChatRestriction::SendVideoMessages, + ChatRestriction::SendMusic, + ChatRestriction::SendVoiceMessages, + ChatRestriction::SendFiles, + }; +} +[[nodiscard]] inline constexpr auto FilesSendRestrictionsList() { + return std::array{ + ChatRestriction::SendStickers, + ChatRestriction::SendGifs, + ChatRestriction::SendPhotos, + ChatRestriction::SendVideos, + ChatRestriction::SendMusic, + ChatRestriction::SendFiles, + }; +} +[[nodiscard]] inline constexpr auto TabbedPanelSendRestrictionsList() { + return std::array{ + ChatRestriction::SendStickers, + ChatRestriction::SendGifs, + ChatRestriction::SendOther, + }; +} +[[nodiscard]] ChatRestrictions AllSendRestrictions(); +[[nodiscard]] ChatRestrictions FilesSendRestrictions(); +[[nodiscard]] ChatRestrictions TabbedPanelSendRestrictions(); + +[[nodiscard]] bool CanSendAnyOf( + not_null thread, + ChatRestrictions rights, + bool forbidInForums = true); +[[nodiscard]] bool CanSendAnyOf( + not_null peer, + ChatRestrictions rights, + bool forbidInForums = true); + +[[nodiscard]] inline bool CanSend( + not_null thread, + ChatRestriction right, + bool forbidInForums = true) { + return CanSendAnyOf(thread, right, forbidInForums); +} +[[nodiscard]] inline bool CanSend( + not_null peer, + ChatRestriction right, + bool forbidInForums = true) { + return CanSendAnyOf(peer, right, forbidInForums); +} +[[nodiscard]] inline bool CanSendTexts( + not_null thread, + bool forbidInForums = true) { + return CanSend(thread, ChatRestriction::SendOther, forbidInForums); +} +[[nodiscard]] inline bool CanSendTexts( + not_null peer, + bool forbidInForums = true) { + return CanSend(peer, ChatRestriction::SendOther, forbidInForums); +} +[[nodiscard]] inline bool CanSendAnything( + not_null thread, + bool forbidInForums = true) { + return CanSendAnyOf(thread, AllSendRestrictions(), forbidInForums); +} +[[nodiscard]] inline bool CanSendAnything( + not_null peer, + bool forbidInForums = true) { + return CanSendAnyOf(peer, AllSendRestrictions(), forbidInForums); +} + +[[nodiscard]] std::optional RestrictionError( + not_null peer, + ChatRestriction restriction); +[[nodiscard]] std::optional AnyFileRestrictionError( + not_null peer); +[[nodiscard]] std::optional FileRestrictionError( + not_null peer, + const Ui::PreparedList &list, + std::optional compress); +[[nodiscard]] std::optional FileRestrictionError( + not_null peer, + const Ui::PreparedFile &file, + std::optional compress); + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 3d69f414b..8009b808b 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -841,6 +841,22 @@ void DocumentData::setLoadedInMediaCache(bool loaded) { } } +ChatRestriction DocumentData::requiredSendRight() const { + return isVideoFile() + ? ChatRestriction::SendVideos + : isSong() + ? ChatRestriction::SendMusic + : isVoiceMessage() + ? ChatRestriction::SendVoiceMessages + : isVideoMessage() + ? ChatRestriction::SendVideoMessages + : sticker() + ? ChatRestriction::SendStickers + : isAnimation() + ? ChatRestriction::SendGifs + : ChatRestriction::SendFiles; +} + void DocumentData::setFileName(const QString &remoteFileName) { _filename = remoteFileName; diff --git a/Telegram/SourceFiles/data/data_document.h b/Telegram/SourceFiles/data/data_document.h index 41dea232f..42d49058d 100644 --- a/Telegram/SourceFiles/data/data_document.h +++ b/Telegram/SourceFiles/data/data_document.h @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_location.h" #include "ui/image/image.h" +enum class ChatRestriction; class mtpFileLoader; namespace Images { @@ -126,6 +127,8 @@ public: [[nodiscard]] bool loadedInMediaCache() const; void setLoadedInMediaCache(bool loaded); + [[nodiscard]] ChatRestriction requiredSendRight() const; + void setWaitingForAlbum(); [[nodiscard]] bool waitingForAlbum() const; diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index 176d07623..bf7ee2ec0 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -249,18 +249,6 @@ bool ForumTopic::my() const { return (_flags & Flag::My); } -bool ForumTopic::canWrite() const { - const auto channel = this->channel(); - return channel->amIn() - && !channel->amRestricted(ChatRestriction::SendMessages) - && (!closed() || canToggleClosed()); -} - -bool ForumTopic::canSendPolls() const { - return canWrite() - && !channel()->amRestricted(ChatRestriction::SendPolls); -} - bool ForumTopic::canEdit() const { return my() || channel()->canManageTopics(); } diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h index e2a631723..295685e76 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.h +++ b/Telegram/SourceFiles/data/data_forum_topic.h @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/flags.h" class ChannelData; +enum class ChatRestriction; namespace style { struct ForumTopicIcon; @@ -80,8 +81,6 @@ public: [[nodiscard]] not_null listMemento(); [[nodiscard]] bool my() const; - [[nodiscard]] bool canWrite() const; - [[nodiscard]] bool canSendPolls() const; [[nodiscard]] bool canEdit() const; [[nodiscard]] bool canToggleClosed() const; [[nodiscard]] bool canTogglePinned() const; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 9fbf014bc..7bee83b80 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -460,10 +460,6 @@ bool Media::forceForwardedInfo() const { return false; } -QString Media::errorTextForForward(not_null peer) const { - return QString(); -} - bool Media::hasSpoiler() const { return false; } @@ -686,13 +682,6 @@ bool MediaPhoto::allowsEditMedia() const { return true; } -QString MediaPhoto::errorTextForForward(not_null peer) const { - return Data::RestrictionError( - peer, - ChatRestriction::SendMedia - ).value_or(QString()); -} - bool MediaPhoto::hasSpoiler() const { return _spoiler; } @@ -1040,46 +1029,6 @@ bool MediaFile::dropForwardedInfo() const { return _document->isSong(); } -QString MediaFile::errorTextForForward(not_null peer) const { - if (const auto sticker = _document->sticker()) { - if (const auto error = Data::RestrictionError( - peer, - ChatRestriction::SendStickers)) { - return *error; - } - } else if (_document->isAnimation()) { - if (_document->isVideoMessage()) { - if (const auto error = Data::RestrictionError( - peer, - ChatRestriction::SendMedia)) { - return *error; - } - if (const auto error = Data::RestrictionError( - peer, - UserRestriction::SendVideoMessages)) { - return *error; - } - } else { - if (const auto error = Data::RestrictionError( - peer, - ChatRestriction::SendGifs)) { - return *error; - } - } - } else if (const auto error = Data::RestrictionError( - peer, - ChatRestriction::SendMedia)) { - return *error; - } else if (_document->isVoiceMessage()) { - if (const auto error = Data::RestrictionError( - peer, - UserRestriction::SendVoiceMessages)) { - return *error; - } - } - return QString(); -} - bool MediaFile::hasSpoiler() const { return _spoiler; } @@ -1589,13 +1538,6 @@ TextForMimeData MediaGame::clipboardText() const { return TextForMimeData(); } -QString MediaGame::errorTextForForward(not_null peer) const { - return Data::RestrictionError( - peer, - ChatRestriction::SendGames - ).value_or(QString()); -} - bool MediaGame::dropForwardedInfo() const { return true; } @@ -1773,16 +1715,6 @@ TextForMimeData MediaPoll::clipboardText() const { return TextForMimeData::Simple(text); } -QString MediaPoll::errorTextForForward(not_null peer) const { - if (_poll->publicVotes() && peer->isChannel() && !peer->isMegagroup()) { - return tr::lng_restricted_send_public_polls(tr::now); - } - return Data::RestrictionError( - peer, - ChatRestriction::SendPolls - ).value_or(QString()); -} - bool MediaPoll::updateInlineResultMedia(const MTPMessageMedia &media) { return false; } @@ -1888,7 +1820,7 @@ ClickHandlerPtr MediaDice::MakeHandler( .durationMs = Ui::Toast::kDefaultDuration * 2, .multiline = true, }; - if (history->peer->canWrite()) { + if (Data::CanSend(history->peer, ChatRestriction::SendOther)) { auto link = Ui::Text::Link( tr::lng_about_random_send(tr::now).toUpper()); link.entities.push_back( diff --git a/Telegram/SourceFiles/data/data_media_types.h b/Telegram/SourceFiles/data/data_media_types.h index 1b69d28f7..a21df7354 100644 --- a/Telegram/SourceFiles/data/data_media_types.h +++ b/Telegram/SourceFiles/data/data_media_types.h @@ -128,7 +128,6 @@ public: virtual bool forwardedBecomesUnread() const; virtual bool dropForwardedInfo() const; virtual bool forceForwardedInfo() const; - virtual QString errorTextForForward(not_null peer) const; [[nodiscard]] virtual bool hasSpoiler() const; [[nodiscard]] virtual bool consumeMessageText( @@ -190,7 +189,6 @@ public: TextForMimeData clipboardText() const override; bool allowsEditCaption() const override; bool allowsEditMedia() const override; - QString errorTextForForward(not_null peer) const override; bool hasSpoiler() const override; bool updateInlineResultMedia(const MTPMessageMedia &media) override; @@ -234,7 +232,6 @@ public: bool allowsEditMedia() const override; bool forwardedBecomesUnread() const override; bool dropForwardedInfo() const override; - QString errorTextForForward(not_null peer) const override; bool hasSpoiler() const override; bool updateInlineResultMedia(const MTPMessageMedia &media) override; @@ -395,7 +392,6 @@ public: TextWithEntities notificationText() const override; QString pinnedTextSubstring() const override; TextForMimeData clipboardText() const override; - QString errorTextForForward(not_null peer) const override; bool dropForwardedInfo() const override; bool consumeMessageText(const TextWithEntities &text) override; @@ -460,7 +456,6 @@ public: TextWithEntities notificationText() const override; QString pinnedTextSubstring() const override; TextForMimeData clipboardText() const override; - QString errorTextForForward(not_null peer) const override; bool updateInlineResultMedia(const MTPMessageMedia &media) override; bool updateSentMedia(const MTPMessageMedia &media) override; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index c4ff6d50e..76c7c3a3f 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -462,7 +462,7 @@ QString PeerData::computeUnavailableReason() const { // This is duplicated in CanPinMessagesValue(). bool PeerData::canPinMessages() const { if (const auto user = asUser()) { - return user->flags() & UserDataFlag::CanPinMessages; + return !user->amRestricted(ChatRestriction::PinMessages); } else if (const auto chat = asChat()) { return chat->amIn() && !chat->amRestricted(ChatRestriction::PinMessages); @@ -881,18 +881,6 @@ Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const { return nullptr; } - -bool PeerData::canWrite(bool checkForForum) const { - if (const auto user = asUser()) { - return user->canWrite(); - } else if (const auto channel = asChannel()) { - return channel->canWrite(checkForForum); - } else if (const auto chat = asChat()) { - return chat->canWrite(); - } - return false; -} - bool PeerData::allowsForwarding() const { if (const auto user = asUser()) { return true; @@ -920,10 +908,26 @@ Data::RestrictionCheckResult PeerData::amRestricted( return chat->hasAdminRights(); } }; - if (const auto channel = asChannel()) { + if (const auto user = asUser()) { + return (right == ChatRestriction::SendVoiceMessages + || right == ChatRestriction::SendVideoMessages) + ? ((user->flags() & UserDataFlag::VoiceMessagesForbidden) + ? Result::Explicit() + : Result::Allowed()) + : (right == ChatRestriction::SendPolls) + ? ((!user->isBot() || user->isSupport()) + ? Result::Explicit() + : Result::Allowed()) + : (right == ChatRestriction::PinMessages) + ? ((user->flags() & UserDataFlag::CanPinMessages) + ? Result::Allowed() + : Result::Explicit()) + : Result::Allowed(); + } else if (const auto channel = asChannel()) { const auto defaultRestrictions = channel->defaultRestrictions() | (channel->isPublic() - ? (ChatRestriction::PinMessages | ChatRestriction::ChangeInfo) + ? (ChatRestriction::PinMessages + | ChatRestriction::ChangeInfo) : ChatRestrictions(0)); return (channel->amCreator() || allowByAdminRights(right, channel)) ? Result::Allowed() @@ -1010,19 +1014,6 @@ int PeerData::slowmodeSecondsLeft() const { return 0; } -bool PeerData::canSendPolls() const { - if (const auto user = asUser()) { - return user->isBot() - && !user->isRepliesChat() - && !user->isSupport(); - } else if (const auto chat = asChat()) { - return chat->canSendPolls(); - } else if (const auto channel = asChannel()) { - return channel->canSendPolls(); - } - return false; -} - bool PeerData::canManageGroupCall() const { if (const auto chat = asChat()) { return chat->amCreator() @@ -1109,95 +1100,6 @@ void PeerData::setMessagesTTL(TimeId period) { namespace Data { -std::optional RestrictionError( - not_null peer, - ChatRestriction restriction) { - using Flag = ChatRestriction; - if (const auto restricted = peer->amRestricted(restriction)) { - const auto all = restricted.isWithEveryone(); - const auto channel = peer->asChannel(); - if (!all && channel) { - auto restrictedUntil = channel->restrictedUntil(); - if (restrictedUntil > 0 && !ChannelData::IsRestrictedForever(restrictedUntil)) { - auto restrictedUntilDateTime = base::unixtime::parse(channel->restrictedUntil()); - auto date = QLocale().toString(restrictedUntilDateTime.date(), QLocale::ShortFormat); - auto time = QLocale().toString(restrictedUntilDateTime.time(), QLocale::ShortFormat); - - switch (restriction) { - case Flag::SendPolls: - return tr::lng_restricted_send_polls_until( - tr::now, lt_date, date, lt_time, time); - case Flag::SendMessages: - return tr::lng_restricted_send_message_until( - tr::now, lt_date, date, lt_time, time); - case Flag::SendMedia: - return tr::lng_restricted_send_media_until( - tr::now, lt_date, date, lt_time, time); - case Flag::SendStickers: - return tr::lng_restricted_send_stickers_until( - tr::now, lt_date, date, lt_time, time); - case Flag::SendGifs: - return tr::lng_restricted_send_gifs_until( - tr::now, lt_date, date, lt_time, time); - case Flag::SendInline: - case Flag::SendGames: - return tr::lng_restricted_send_inline_until( - tr::now, lt_date, date, lt_time, time); - } - Unexpected("Restriction in Data::RestrictionErrorKey."); - } - } - switch (restriction) { - case Flag::SendPolls: - return all - ? tr::lng_restricted_send_polls_all(tr::now) - : tr::lng_restricted_send_polls(tr::now); - case Flag::SendMessages: - return all - ? tr::lng_restricted_send_message_all(tr::now) - : tr::lng_restricted_send_message(tr::now); - case Flag::SendMedia: - return all - ? tr::lng_restricted_send_media_all(tr::now) - : tr::lng_restricted_send_media(tr::now); - case Flag::SendStickers: - return all - ? tr::lng_restricted_send_stickers_all(tr::now) - : tr::lng_restricted_send_stickers(tr::now); - case Flag::SendGifs: - return all - ? tr::lng_restricted_send_gifs_all(tr::now) - : tr::lng_restricted_send_gifs(tr::now); - case Flag::SendInline: - case Flag::SendGames: - return all - ? tr::lng_restricted_send_inline_all(tr::now) - : tr::lng_restricted_send_inline(tr::now); - } - Unexpected("Restriction in Data::RestrictionErrorKey."); - } - return std::nullopt; -} - -std::optional RestrictionError( - not_null peer, - UserRestriction restriction) { - const auto user = peer->asUser(); - if (user && !user->canReceiveVoices()) { - const auto voice = restriction == UserRestriction::SendVoiceMessages; - if (voice - || (restriction == UserRestriction::SendVideoMessages)) { - return (voice - ? tr::lng_restricted_send_voice_messages - : tr::lng_restricted_send_video_messages)( - tr::now, - lt_user, - user->name()); - } - } - return std::nullopt; -} - void SetTopPinnedMessageId( not_null peer, MsgId messageId) { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 8a55be831..5ee162bbf 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -20,7 +20,6 @@ class ChatData; class ChannelData; enum class ChatRestriction; -enum class UserRestriction; namespace Ui { class EmptyUserpic; @@ -205,7 +204,6 @@ public: return _notify; } - [[nodiscard]] bool canWrite(bool checkForForum = true) const; [[nodiscard]] bool allowsForwarding() const; [[nodiscard]] Data::RestrictionCheckResult amRestricted( ChatRestriction right) const; @@ -214,7 +212,6 @@ public: [[nodiscard]] bool slowmodeApplied() const; [[nodiscard]] rpl::producer slowmodeAppliedValue() const; [[nodiscard]] int slowmodeSecondsLeft() const; - [[nodiscard]] bool canSendPolls() const; [[nodiscard]] bool canManageGroupCall() const; [[nodiscard]] UserData *asUser(); @@ -454,14 +451,6 @@ private: namespace Data { -std::optional RestrictionError( - not_null peer, - ChatRestriction restriction); - -std::optional RestrictionError( - not_null peer, - UserRestriction restriction); - void SetTopPinnedMessageId( not_null peer, MsgId messageId); diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 6004a704b..c7622ea24 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -164,129 +164,136 @@ inline auto DefaultRestrictionValue( return SingleFlagValue(DefaultRestrictionsValue(chat), flag); } -rpl::producer CanWriteValue(UserData *user) { - using namespace rpl::mappers; - - if (user->isRepliesChat()) { - return rpl::single(false); +// Duplicated in CanSendAnyOf(). +[[nodiscard]] rpl::producer CanSendAnyOfValue( + not_null thread, + ChatRestrictions rights, + bool forbidInForums) { + if (const auto topic = thread->asTopic()) { + using Flag = ChannelDataFlag; + const auto mask = Flag() + | Flag::Left + | Flag::JoinToWrite + | Flag::HasLink + | Flag::Forbidden + | Flag::Creator; + const auto channel = topic->channel(); + return rpl::combine( + PeerFlagsValue(channel.get(), mask), + RestrictionsValue(channel, rights), + DefaultRestrictionsValue(channel, rights), + AdminRightsValue(channel, ChatAdminRight::ManageTopics), + topic->session().changes().topicFlagsValue( + topic, + TopicUpdate::Flag::Closed), + [=]( + ChannelDataFlags flags, + ChatRestrictions sendRestriction, + ChatRestrictions defaultSendRestriction, + auto, + auto) { + const auto notAmInFlags = Flag::Left | Flag::Forbidden; + const auto allowed = !(flags & notAmInFlags) + || ((flags & Flag::HasLink) + && !(flags & Flag::JoinToWrite)); + return allowed + && ((flags & Flag::Creator) + || (!sendRestriction && !defaultSendRestriction)) + && (!topic->closed() || topic->canToggleClosed()); + }); } - return PeerFlagValue(user, UserDataFlag::Deleted) - | rpl::map(!_1); + return CanSendAnyOfValue(thread->peer(), rights, forbidInForums); } -rpl::producer CanWriteValue(ChatData *chat) { - using namespace rpl::mappers; - const auto mask = 0 - | ChatDataFlag::Deactivated - | ChatDataFlag::Forbidden - | ChatDataFlag::Left - | ChatDataFlag::Creator; - return rpl::combine( - PeerFlagsValue(chat, mask), - AdminRightsValue(chat), - DefaultRestrictionValue( - chat, - ChatRestriction::SendMessages), - []( +// Duplicated in CanSendAnyOf(). +[[nodiscard]] rpl::producer CanSendAnyOfValue( + not_null peer, + ChatRestrictions rights, + bool forbidInForums) { + if (const auto user = peer->asUser()) { + if (user->isRepliesChat()) { + return rpl::single(false); + } + using namespace rpl::mappers; + const auto other = rights & ~(ChatRestriction::SendPolls + | ChatRestriction::SendVoiceMessages + | ChatRestriction::SendVideoMessages); + if (other) { + return PeerFlagValue(user, UserDataFlag::Deleted) + | rpl::map(!_1); + } else if (rights & ChatRestriction::SendPolls) { + if (CanSend(user, ChatRestriction::SendPolls)) { + return PeerFlagValue(user, UserDataFlag::Deleted) + | rpl::map(!_1); + } else if (rights == ChatRestriction::SendPolls) { + return rpl::single(false); + } + } + const auto mask = UserDataFlag::Deleted + | UserDataFlag::VoiceMessagesForbidden; + return PeerFlagsValue(user, mask) | rpl::map(!_1); + } else if (const auto chat = peer->asChat()) { + const auto mask = ChatDataFlag() + | ChatDataFlag::Deactivated + | ChatDataFlag::Forbidden + | ChatDataFlag::Left + | ChatDataFlag::Creator; + return rpl::combine( + PeerFlagsValue(chat, mask), + AdminRightsValue(chat), + DefaultRestrictionsValue(chat, rights), + [rights]( ChatDataFlags flags, Data::Flags::Change adminRights, - bool defaultSendMessagesRestriction) { - const auto amOutFlags = 0 + ChatRestrictions defaultSendRestrictions) { + const auto amOutFlags = ChatDataFlag() | ChatDataFlag::Deactivated | ChatDataFlag::Forbidden | ChatDataFlag::Left; - return !(flags & amOutFlags) - && ((flags & ChatDataFlag::Creator) - || (adminRights.value != ChatAdminRights(0)) - || !defaultSendMessagesRestriction); + return !(flags & amOutFlags) + && ((flags & ChatDataFlag::Creator) + || (adminRights.value != ChatAdminRights(0)) + || (rights & ~defaultSendRestrictions)); }); -} - -rpl::producer CanWriteValue(ChannelData *channel, bool checkForForum) { - using Flag = ChannelDataFlag; - const auto mask = 0 - | Flag::Left - | Flag::Forum - | Flag::JoinToWrite - | Flag::HasLink - | Flag::Forbidden - | Flag::Creator - | Flag::Broadcast; - return rpl::combine( - PeerFlagsValue(channel, mask), - AdminRightValue( - channel, - ChatAdminRight::PostMessages), - RestrictionValue( - channel, - ChatRestriction::SendMessages), - DefaultRestrictionValue( - channel, - ChatRestriction::SendMessages), - [=]( - ChannelDataFlags flags, - bool postMessagesRight, - bool sendMessagesRestriction, - bool defaultSendMessagesRestriction) { - const auto notAmInFlags = Flag::Left | Flag::Forbidden; - const auto forumRestriction = checkForForum - && (flags & Flag::Forum); - const auto allowed = !(flags & notAmInFlags) - || ((flags & Flag::HasLink) && !(flags & Flag::JoinToWrite)); - return allowed - && !forumRestriction - && (postMessagesRight - || (flags & Flag::Creator) - || (!(flags & Flag::Broadcast) - && !sendMessagesRestriction - && !defaultSendMessagesRestriction)); - }); -} - -rpl::producer CanWriteValue( - not_null peer, - bool checkForForum) { - if (auto user = peer->asUser()) { - return CanWriteValue(user); - } else if (auto chat = peer->asChat()) { - return CanWriteValue(chat); - } else if (auto channel = peer->asChannel()) { - return CanWriteValue(channel, checkForForum); + } else if (const auto channel = peer->asChannel()) { + using Flag = ChannelDataFlag; + const auto mask = Flag() + | Flag::Left + | Flag::Forum + | Flag::JoinToWrite + | Flag::HasLink + | Flag::Forbidden + | Flag::Creator + | Flag::Broadcast; + return rpl::combine( + PeerFlagsValue(channel, mask), + AdminRightValue( + channel, + ChatAdminRight::PostMessages), + RestrictionsValue(channel, rights), + DefaultRestrictionsValue(channel, rights), + [=]( + ChannelDataFlags flags, + bool postMessagesRight, + ChatRestrictions sendRestriction, + ChatRestrictions defaultSendRestriction) { + const auto notAmInFlags = Flag::Left | Flag::Forbidden; + const auto forumRestriction = forbidInForums + && (flags & Flag::Forum); + const auto allowed = !(flags & notAmInFlags) + || ((flags & Flag::HasLink) + && !(flags & Flag::JoinToWrite)); + const auto restricted = sendRestriction + | defaultSendRestriction; + return allowed + && !forumRestriction + && (postMessagesRight + || (flags & Flag::Creator) + || (!(flags & Flag::Broadcast) + && (rights & ~restricted))); + }); } - Unexpected("Bad peer value in CanWriteValue"); -} - -rpl::producer CanWriteValue(not_null 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()); - }); + Unexpected("Peer type in Data::CanSendAnyOfValue."); } // This is duplicated in PeerData::canPinMessages(). diff --git a/Telegram/SourceFiles/data/data_peer_values.h b/Telegram/SourceFiles/data/data_peer_values.h index 673a6b846..ad20d8243 100644 --- a/Telegram/SourceFiles/data/data_peer_values.h +++ b/Telegram/SourceFiles/data/data_peer_values.h @@ -7,10 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include -#include -#include #include "data/data_peer.h" +#include "data/data_chat_participant_status.h" enum class ImageRoundRadius; @@ -53,6 +51,7 @@ template < typename ChangeType = typename PeerType::Flags::Change> inline auto PeerFlagsValue(PeerType *peer) { Expects(peer != nullptr); + return peer->flagsValue(); } @@ -101,15 +100,48 @@ inline auto PeerFullFlagValue( return SingleFlagValue(PeerFullFlagsValue(peer), flag); } -[[nodiscard]] rpl::producer CanWriteValue(UserData *user); -[[nodiscard]] rpl::producer CanWriteValue(ChatData *chat); -[[nodiscard]] rpl::producer CanWriteValue( - ChannelData *channel, - bool checkForForum = true); -[[nodiscard]] rpl::producer CanWriteValue( +[[nodiscard]] rpl::producer CanSendAnyOfValue( + not_null thread, + ChatRestrictions rights, + bool forbidInForums = true); +[[nodiscard]] rpl::producer CanSendAnyOfValue( not_null peer, - bool checkForForum = true); -[[nodiscard]] rpl::producer CanWriteValue(not_null topic); + ChatRestrictions rights, + bool forbidInForums = true); + +[[nodiscard]] inline rpl::producer CanSendValue( + not_null thread, + ChatRestriction right, + bool forbidInForums = true) { + return CanSendAnyOfValue(thread, right, forbidInForums); +} +[[nodiscard]] inline rpl::producer CanSendValue( + not_null peer, + ChatRestriction right, + bool forbidInForums = true) { + return CanSendAnyOfValue(peer, right, forbidInForums); +} +[[nodiscard]] inline rpl::producer CanSendTextsValue( + not_null thread, + bool forbidInForums = true) { + return CanSendValue(thread, ChatRestriction::SendOther, forbidInForums); +} +[[nodiscard]] inline rpl::producer CanSendTextsValue( + not_null peer, + bool forbidInForums = true) { + return CanSendValue(peer, ChatRestriction::SendOther, forbidInForums); +} +[[nodiscard]] inline rpl::producer CanSendAnythingValue( + not_null thread, + bool forbidInForums = true) { + return CanSendAnyOfValue(thread, AllSendRestrictions(), forbidInForums); +} +[[nodiscard]] inline rpl::producer CanSendAnythingValue( + not_null peer, + bool forbidInForums = true) { + return CanSendAnyOfValue(peer, AllSendRestrictions(), forbidInForums); +} + [[nodiscard]] rpl::producer CanPinMessagesValue( not_null peer); [[nodiscard]] rpl::producer CanManageGroupCallValue( diff --git a/Telegram/SourceFiles/data/data_scheduled_messages.cpp b/Telegram/SourceFiles/data/data_scheduled_messages.cpp index e33cad8e0..b8438d123 100644 --- a/Telegram/SourceFiles/data/data_scheduled_messages.cpp +++ b/Telegram/SourceFiles/data/data_scheduled_messages.cpp @@ -392,7 +392,7 @@ Data::MessagesSlice ScheduledMessages::list(not_null history) { void ScheduledMessages::request(not_null history) { const auto peer = history->peer; - if (peer->isBroadcast() && !peer->canWrite()) { + if (peer->isBroadcast() && !Data::CanSendAnything(peer)) { return; } auto &request = _requests[history]; diff --git a/Telegram/SourceFiles/data/data_thread.cpp b/Telegram/SourceFiles/data/data_thread.cpp index 7f86892c9..1934c3450 100644 --- a/Telegram/SourceFiles/data/data_thread.cpp +++ b/Telegram/SourceFiles/data/data_thread.cpp @@ -44,11 +44,6 @@ const PeerNotifySettings &Thread::notify() const { return const_cast(this)->notify(); } -bool Thread::canWrite() const { - const auto topic = asTopic(); - return topic ? topic->canWrite() : peer()->canWrite(); -} - void Thread::setUnreadThingsKnown() { _flags |= Flag::UnreadThingsKnown; } diff --git a/Telegram/SourceFiles/data/data_thread.h b/Telegram/SourceFiles/data/data_thread.h index f0808a5a2..9bbc6635f 100644 --- a/Telegram/SourceFiles/data/data_thread.h +++ b/Telegram/SourceFiles/data/data_thread.h @@ -7,12 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/flags.h" #include "dialogs/dialogs_entry.h" #include "dialogs/ui/dialogs_message_view.h" #include "ui/text/text.h" #include +enum class ChatRestriction; +using ChatRestrictions = base::flags; + namespace Main { class Session; } // namespace Main @@ -67,7 +71,6 @@ public: [[nodiscard]] PeerNotifySettings ¬ify(); [[nodiscard]] const PeerNotifySettings ¬ify() const; - [[nodiscard]] bool canWrite() const; void setUnreadThingsKnown(); [[nodiscard]] HistoryUnreadThings::Proxy unreadMentions(); [[nodiscard]] HistoryUnreadThings::ConstProxy unreadMentions() const; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index b4e59f2f2..8de9bd35b 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -293,11 +293,6 @@ bool UserData::isInaccessible() const { return flags() & UserDataFlag::Deleted; } -bool UserData::canWrite() const { - // Duplicated in Data::CanWriteValue(). - return !isInaccessible() && !isRepliesChat(); -} - bool UserData::applyMinPhoto() const { return !(flags() & UserDataFlag::DiscardMinPhoto); } @@ -314,10 +309,6 @@ bool UserData::canReceiveGifts() const { return flags() & UserDataFlag::CanReceiveGifts; } -bool UserData::canReceiveVoices() const { - return !(flags() & UserDataFlag::VoiceMessagesForbidden); -} - bool UserData::canShareThisContactFast() const { return !_phone.isEmpty(); } diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index 04ba6ed40..a51001095 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -112,7 +112,6 @@ public: [[nodiscard]] bool isBot() const; [[nodiscard]] bool isSupport() const; [[nodiscard]] bool isInaccessible() const; - [[nodiscard]] bool canWrite() const; [[nodiscard]] bool applyMinPhoto() const; [[nodiscard]] bool hasPersonalPhoto() const; @@ -120,7 +119,6 @@ public: [[nodiscard]] bool canAddContact() const; [[nodiscard]] bool canReceiveGifts() const; - [[nodiscard]] bool canReceiveVoices() const; // In Data::Session::processUsers() we check only that. // When actually trying to share contact we perform diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 049d8f4e3..b92ed2736 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -255,8 +255,17 @@ QString GeneratePermissionsChangeText( static auto phraseMap = std::map>{ { Flag::ViewMessages, tr::lng_admin_log_banned_view_messages }, - { Flag::SendMessages, tr::lng_admin_log_banned_send_messages }, - { Flag::SendMedia, tr::lng_admin_log_banned_send_media }, + { Flag::SendOther, tr::lng_admin_log_banned_send_messages }, + { Flag::SendPhotos, tr::lng_admin_log_banned_send_photos }, + { Flag::SendVideos, tr::lng_admin_log_banned_send_videos }, + { Flag::SendMusic, tr::lng_admin_log_banned_send_music }, + { Flag::SendFiles, tr::lng_admin_log_banned_send_files }, + { + Flag::SendVoiceMessages, + tr::lng_admin_log_banned_send_voice_messages }, + { + Flag::SendVideoMessages, + tr::lng_admin_log_banned_send_video_messages }, { Flag::SendStickers | Flag::SendGifs | Flag::SendInline diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 032ba3b1d..339b42ada 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -947,13 +947,13 @@ not_null History::addNewToBack( if (peer->isChat()) { botNotInChat = item->from()->isUser() && (!peer->asChat()->participants.empty() - || !peer->canWrite()) + || !Data::CanSendAnything(peer)) && !peer->asChat()->participants.contains( item->from()->asUser()); } else if (peer->isMegagroup()) { botNotInChat = item->from()->isUser() && (peer->asChannel()->mgInfo->botStatus != 0 - || !peer->canWrite()) + || !Data::CanSendAnything(peer)) && !peer->asChannel()->mgInfo->bots.contains( item->from()->asUser()); } @@ -1501,9 +1501,15 @@ void History::addItemsToLists( if (!lastKeyboardInited) { bool botNotInChat = false; if (peer->isChat()) { - botNotInChat = (!peer->canWrite() || !peer->asChat()->participants.empty()) && item->author()->isUser() && !peer->asChat()->participants.contains(item->author()->asUser()); + botNotInChat = (!Data::CanSendAnything(peer) + || !peer->asChat()->participants.empty()) + && item->author()->isUser() + && !peer->asChat()->participants.contains(item->author()->asUser()); } else if (peer->isMegagroup()) { - botNotInChat = (!peer->canWrite() || peer->asChannel()->mgInfo->botStatus != 0) && item->author()->isUser() && !peer->asChannel()->mgInfo->bots.contains(item->author()->asUser()); + botNotInChat = (!Data::CanSendAnything(peer) + || peer->asChannel()->mgInfo->botStatus != 0) + && item->author()->isUser() + && !peer->asChannel()->mgInfo->bots.contains(item->author()->asUser()); } if (wasKeyboardHide || botNotInChat) { clearLastKeyboard(); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 26ada37ac..069f785d0 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2070,7 +2070,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { const auto canReply = [&] { const auto peer = item->history()->peer; const auto topic = item->topic(); - return topic ? topic->canWrite() : peer->canWrite(); + return topic + ? Data::CanSendAnything(topic) + : Data::CanSendAnything(peer); }(); if (canReply) { _menu->addAction(tr::lng_context_reply_msg(tr::now), [=] { diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index eb061f594..9c5d78036 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -63,6 +63,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_game.h" #include "data/data_user.h" #include "data/data_group_call.h" // Data::GroupCall::id(). +#include "data/data_poll.h" // PollData::publicVotes. #include "data/data_sponsored_messages.h" #include "data/data_web_page.h" #include "chat_helpers/stickers_gift_box_pack.h" @@ -1836,9 +1837,9 @@ bool HistoryItem::canBeEdited() const { if (isPost()) { return channel->canPublish(); } else if (const auto topic = this->topic()) { - return topic->canWrite(); + return Data::CanSendAnything(topic); } else { - return channel->canWrite(); + return Data::CanSendAnything(channel); } } else { return false; @@ -1958,6 +1959,53 @@ bool HistoryItem::suggestDeleteAllReport() const { return !isPost() && !out(); } +ChatRestriction HistoryItem::requiredSendRight() const { + const auto media = this->media(); + if (media && media->game()) { + return ChatRestriction::SendGames; + } + const auto photo = (media && !media->webpage()) + ? media->photo() + : nullptr; + const auto document = (media && !media->webpage()) + ? media->document() + : nullptr; + if (photo) { + return ChatRestriction::SendPhotos; + } else if (document) { + return document->requiredSendRight(); + } else if (media && media->poll()) { + return ChatRestriction::SendPolls; + } + return ChatRestriction::SendOther; +} + +bool HistoryItem::requiresSendInlineRight() const { + return Has(); +} + +std::optional HistoryItem::errorTextForForward( + not_null to) const { + const auto requiredRight = requiredSendRight(); + const auto requiresInline = requiresSendInlineRight(); + const auto peer = to->peer(); + constexpr auto kInline = ChatRestriction::SendInline; + if (const auto error = Data::RestrictionError(peer, requiredRight)) { + return *error; + } else if (requiresInline && !Data::CanSend(to, kInline)) { + return Data::RestrictionError(peer, kInline).value_or( + tr::lng_forward_cant(tr::now)); + } else if (_media + && _media->poll() + && _media->poll()->publicVotes() + && peer->isBroadcast()) { + return tr::lng_restricted_send_public_polls(tr::now); + } else if (!Data::CanSend(to, requiredRight, false)) { + return tr::lng_forward_cant(tr::now); + } + return {}; +} + bool HistoryItem::canReact() const { if (!isRegular() || isService()) { return false; diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 05041d4c6..bace65d08 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -393,6 +393,10 @@ public: [[nodiscard]] bool suggestReport() const; [[nodiscard]] bool suggestBanReport() const; [[nodiscard]] bool suggestDeleteAllReport() const; + [[nodiscard]] ChatRestriction requiredSendRight() const; + [[nodiscard]] bool requiresSendInlineRight() const; + [[nodiscard]] std::optional errorTextForForward( + not_null to) const; [[nodiscard]] bool canReact() const; enum class ReactionSource { diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index dd569d5c8..cac95deeb 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -41,15 +41,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -[[nodiscard]] bool HasInlineItems(const HistoryItemsList &items) { - for (const auto &item : items) { - if (item->viaBot()) { - return true; - } - } - return false; -} - bool PeerCallKnown(not_null peer) { if (peer->groupCall() != nullptr) { return true; @@ -70,29 +61,28 @@ QString GetErrorTextForSending( const auto topic = forum ? forum->topicFor(request.topicRootId) : nullptr; - if (!(topic ? topic->canWrite() : peer->canWrite())) { - return tr::lng_forward_cant(tr::now); - } - + const auto thread = topic + ? not_null(topic) + : peer->owner().history(peer); if (request.forward) { for (const auto &item : *request.forward) { - if (const auto media = item->media()) { - const auto error = media->errorTextForForward(peer); - if (!error.isEmpty() && error != u"skip"_q) { - return error; - } + if (const auto error = item->errorTextForForward(thread)) { + return *error; } } } - const auto error = Data::RestrictionError( - peer, - ChatRestriction::SendInline); - if (error && request.forward && HasInlineItems(*request.forward)) { - return *error; + const auto hasText = (request.text && !request.text->empty()); + if (hasText) { + const auto error = Data::RestrictionError( + peer, + ChatRestriction::SendOther); + if (error) { + return *error; + } else if (!Data::CanSendTexts(thread)) { + return tr::lng_forward_cant(tr::now); + } } - 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)) { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index f10a21d77..4d72a591b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -25,8 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_peer_requests_box.h" #include "core/file_utilities.h" #include "core/mime_type.h" -#include "ui/toast/toast.h" -#include "ui/toasts/common_toasts.h" #include "ui/emoji_config.h" #include "ui/chat/attach/attach_prepare.h" #include "ui/chat/choose_theme_controller.h" @@ -726,7 +724,7 @@ HistoryWidget::HistoryWidget( const auto account = &_peer->account(); closeCurrent(); if (const auto primary = Core::App().windowFor(account)) { - primary->show(Ui::MakeInformBox(unavailable)); + controller->showToast({ unavailable }); } return; } @@ -940,19 +938,14 @@ void HistoryWidget::initVoiceRecordBar() { if (_peer) { if (const auto error = Data::RestrictionError( _peer, - ChatRestriction::SendMedia)) { - return error; - } - if (const auto error = Data::RestrictionError( - _peer, - UserRestriction::SendVoiceMessages)) { + ChatRestriction::SendVoiceMessages)) { return error; } } return std::nullopt; }(); if (error) { - controller()->show(Ui::MakeInformBox(*error)); + controller()->showToast({ *error }); return true; } else if (showSlowmodeError()) { return true; @@ -1013,10 +1006,7 @@ void HistoryWidget::initVoiceRecordBar() { _voiceRecordBar->recordingTipRequests( ) | rpl::start_with_next([=] { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { tr::lng_record_hold_tip(tr::now) }, - }); + controller()->showToast({ tr::lng_record_hold_tip(tr::now) }); }, lifetime()); _voiceRecordBar->hideFast(); @@ -1564,10 +1554,7 @@ void HistoryWidget::toggleChooseChatTheme(not_null peer) { } return; } else if (_voiceRecordBar->isActive()) { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { tr::lng_chat_theme_cant_voice(tr::now) }, - }); + controller()->showToast({ tr::lng_chat_theme_cant_voice(tr::now) }); return; } _chooseTheme = std::make_unique( @@ -2144,7 +2131,7 @@ void HistoryWidget::showHistory( if (peerId) { _peer = session().data().peer(peerId); - _canSendMessages = _peer->canWrite(); + _canSendMessages = Data::CanSendAnything(_peer); _contactStatus = std::make_unique( controller(), this, @@ -2621,8 +2608,10 @@ bool HistoryWidget::canWriteMessage() const { } std::optional HistoryWidget::writeRestriction() const { - auto result = _peer - ? Data::RestrictionError(_peer, ChatRestriction::SendMessages) + const auto allWithoutPolls = Data::AllSendRestrictions() + & ~ChatRestriction::SendPolls; + auto result = (_peer && !Data::CanSendAnyOf(_peer, allWithoutPolls)) + ? Data::RestrictionError(_peer, ChatRestriction::SendOther) : std::nullopt; if (result) { return result; @@ -2827,7 +2816,15 @@ void HistoryWidget::updateControlsVisibility() { _botMenuButton->hide(); } _kbScroll->hide(); - _fieldBarCancel->hide(); + if (_replyToId || readyToForward() || _kbReplyTo) { + if (_fieldBarCancel->isHidden()) { + _fieldBarCancel->show(); + updateControlsGeometry(); + update(); + } + } else { + _fieldBarCancel->hide(); + } _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -3010,12 +3007,9 @@ void HistoryWidget::messagesFailed(const MTP::Error &error, int requestId) { auto was = _peer; closeCurrent(); if (const auto primary = Core::App().windowFor(&was->account())) { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(primary).toastParent(), - .text = { (was && was->isMegagroup()) - ? tr::lng_group_not_accessible(tr::now) - : tr::lng_channel_not_accessible(tr::now) }, - }); + controller()->showToast({ (was && was->isMegagroup()) + ? tr::lng_group_not_accessible(tr::now) + : tr::lng_channel_not_accessible(tr::now) }); } return; } @@ -3653,8 +3647,9 @@ void HistoryWidget::saveEditMsg() { return; } else if (!left.text.isEmpty()) { const auto remove = left.text.size(); - controller()->show(Ui::MakeInformBox( - tr::lng_edit_limit_reached(tr::now, lt_count, remove))); + controller()->showToast({ + tr::lng_edit_limit_reached(tr::now, lt_count, remove) + }); return; } @@ -3687,14 +3682,14 @@ void HistoryWidget::saveEditMsg() { _saveEditMsgRequestId = 0; } if (ranges::contains(Api::kDefaultEditMessagesErrors, error)) { - controller()->show(Ui::MakeInformBox(tr::lng_edit_error())); + controller()->showToast({ tr::lng_edit_error(tr::now) }); } else if (error == u"MESSAGE_NOT_MODIFIED"_q) { cancelEdit(); } else if (error == u"MESSAGE_EMPTY"_q) { _field->selectAll(); _field->setFocus(); } else { - controller()->show(Ui::MakeInformBox(tr::lng_edit_error())); + controller()->showToast({ tr::lng_edit_error(tr::now) }); } update(); })(); @@ -3796,27 +3791,12 @@ void HistoryWidget::send(Api::SendOptions options) { message.textWithTags = _field->getTextWithAppliedMarkdown(); message.webPageId = webPageId; - if (_canSendMessages) { - const auto topicRootId = _replyEditMsg - ? _replyEditMsg->topicRootId() - : 0; - const auto error = GetErrorTextForSending( - _peer, - { - .topicRootId = topicRootId, - .forward = &_forwardPanel->items(), - .text = &message.textWithTags, - .ignoreSlowmodeCountdown = (options.scheduled != 0), - }); - if (!error.isEmpty()) { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { error }, - }); - return; - } + const auto ignoreSlowmodeCountdown = (options.scheduled != 0); + if (showSendMessageError( + message.textWithTags, + ignoreSlowmodeCountdown)) { + return; } - session().api().sendMessage(std::move(message)); clearFieldText(); @@ -3851,6 +3831,12 @@ void HistoryWidget::sendScheduled() { if (!_list) { return; } + const auto ignoreSlowmodeCountdown = true; + if (showSendMessageError( + _field->getTextWithAppliedMarkdown(), + ignoreSlowmodeCountdown)) { + return; + } const auto callback = [=](Api::SendOptions options) { send(options); }; controller()->show( HistoryView::PrepareScheduleBox(_list, sendMenuType(), callback), @@ -4127,19 +4113,14 @@ void HistoryWidget::finishAnimating() { void HistoryWidget::chooseAttach( std::optional overrideSendImagesAsPhotos) { if (_editMsgId) { - controller()->show(Ui::MakeInformBox(tr::lng_edit_caption_attach())); + controller()->showToast({ tr::lng_edit_caption_attach(tr::now) }); return; } if (!_peer || !_canSendMessages) { return; - } else if (const auto error = Data::RestrictionError( - _peer, - ChatRestriction::SendMedia)) { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { *error }, - }); + } else if (const auto error = Data::AnyFileRestrictionError(_peer)) { + controller()->showToast({ *error }); return; } else if (showSlowmodeError()) { return; @@ -4371,9 +4352,8 @@ bool HistoryWidget::readyToForward() const { bool HistoryWidget::hasSilentToggle() const { return _peer - && _peer->isChannel() - && !_peer->isMegagroup() - && _peer->canWrite() + && _peer->isBroadcast() + && Data::CanSendAnything(_peer) && !session().data().notifySettings().silentPostsUnknown(_peer); } @@ -4421,7 +4401,7 @@ bool HistoryWidget::isChoosingTheme() const { bool HistoryWidget::isMuteUnmute() const { return _peer && ((_peer->isBroadcast() && !_peer->asChannel()->canPublish()) - || (_peer->isGigagroup() && !_peer->asChannel()->canWrite()) + || (_peer->isGigagroup() && !Data::CanSendAnything(_peer)) || _peer->isRepliesChat()); } @@ -4725,9 +4705,15 @@ void HistoryWidget::showMembersDropdown() { bool HistoryWidget::pushTabbedSelectorToThirdSection( not_null thread, const Window::SectionShow ¶ms) { + const auto selectorTypes = ChatRestriction::SendOther + | ChatRestriction::SendInline + | ChatRestriction::SendStickers + | ChatRestriction::SendGifs; if (!_tabbedPanel) { return true; - } else if (!thread->canWrite()) { + } else if (!Data::CanSendAnyOf( + thread, + Data::TabbedPanelSendRestrictions())) { Core::App().settings().setTabbedReplacedWithInfo(true); controller()->showPeerInfo(thread, params.withThirdColumn()); return false; @@ -4978,19 +4964,18 @@ void HistoryWidget::updateFieldPlaceholder() { bool HistoryWidget::showSendingFilesError( const Ui::PreparedList &list) const { + return showSendingFilesError(list, std::nullopt); +} + +bool HistoryWidget::showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const { const auto text = [&] { const auto error = _peer - ? Data::RestrictionError( - _peer, - ChatRestriction::SendMedia) + ? Data::FileRestrictionError(_peer, list, compress) : std::nullopt; if (error) { return *error; - } else if (!canWriteMessage()) { - return tr::lng_forward_send_files_cant(tr::now); - } - if (_peer->slowmodeApplied() && !list.canBeSentInSlowmode()) { - return tr::lng_slowmode_no_many(tr::now); } else if (const auto left = _peer->slowmodeSecondsLeft()) { return tr::lng_slowmode_enabled( tr::now, @@ -5017,11 +5002,31 @@ bool HistoryWidget::showSendingFilesError( controller()->show(Box(FileSizeLimitBox, &session(), fileSize)); return true; } + controller()->showToast({ text }); + return true; +} - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { text }, - }); +bool HistoryWidget::showSendMessageError( + const TextWithTags &textWithTags, + bool ignoreSlowmodeCountdown) const { + if (!_canSendMessages) { + return false; + } + const auto topicRootId = _replyEditMsg + ? _replyEditMsg->topicRootId() + : 0; + const auto error = GetErrorTextForSending( + _peer, + { + .topicRootId = topicRootId, + .forward = &_forwardPanel->items(), + .text = &textWithTags, + .ignoreSlowmodeCountdown = ignoreSlowmodeCountdown, + }); + if (error.isEmpty()) { + return false; + } + controller()->showToast({ error }); return true; } @@ -5045,11 +5050,10 @@ bool HistoryWidget::confirmSendingFiles( bool HistoryWidget::confirmSendingFiles( Ui::PreparedList &&list, const QString &insertTextOnCancel) { - if (showSendingFilesError(list)) { - return false; - } if (_editMsgId) { - controller()->show(Ui::MakeInformBox(tr::lng_edit_caption_attach())); + controller()->showToast({ tr::lng_edit_caption_attach(tr::now) }); + return false; + } else if (showSendingFilesError(list)) { return false; } @@ -5061,7 +5065,8 @@ bool HistoryWidget::confirmSendingFiles( controller(), std::move(list), text, - _peer, + DefaultLimitsForPeer(_peer), + DefaultCheckForPeer(controller(), _peer), Api::SendType::Normal, sendMenuType()); _field->setTextWithTags({}); @@ -5106,16 +5111,15 @@ void HistoryWidget::sendingFilesConfirmed( bool ctrlShiftEnter) { Expects(list.filesToProcess.empty()); - if (showSendingFilesError(list)) { + const auto compress = way.sendImagesAsPhotos(); + if (showSendingFilesError(list, compress)) { return; } auto groups = DivideByGroups( std::move(list), way, _peer->slowmodeApplied()); - const auto type = way.sendImagesAsPhotos() - ? SendMediaType::Photo - : SendMediaType::File; + const auto type = compress ? SendMediaType::Photo : SendMediaType::File; auto action = prepareSendAction(options); action.clearDraft = false; if ((groups.size() != 1 || !groups.front().sentWithCaption()) @@ -5412,10 +5416,7 @@ int HistoryWidget::countInitialScrollTop() { const auto itemTop = _list->itemTop(item); if (itemTop < 0) { setMsgId(0); - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { tr::lng_message_not_found(tr::now) }, - }); + controller()->showToast({ tr::lng_message_not_found(tr::now) }); return countInitialScrollTop(); } else { const auto view = item->mainView(); @@ -6112,10 +6113,7 @@ bool HistoryWidget::showSlowmodeError() { if (text.isEmpty()) { return false; } - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { text }, - }); + controller()->showToast({ text }); return true; } @@ -6136,7 +6134,7 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { auto errorText = result.result->getErrorOnSend(_history); if (!errorText.isEmpty()) { - controller()->show(Ui::MakeInformBox(errorText)); + controller()->showToast({ errorText }); return; } @@ -6602,9 +6600,7 @@ bool HistoryWidget::sendExistingDocument( ? Data::RestrictionError(_peer, ChatRestriction::SendStickers) : std::nullopt; if (error) { - controller()->show( - Ui::MakeInformBox(*error), - Ui::LayerOption::KeepOther); + controller()->showToast({ *error }); return false; } else if (!_peer || !_canSendMessages @@ -6636,12 +6632,10 @@ bool HistoryWidget::sendExistingPhoto( not_null photo, Api::SendOptions options) { const auto error = _peer - ? Data::RestrictionError(_peer, ChatRestriction::SendMedia) + ? Data::RestrictionError(_peer, ChatRestriction::SendPhotos) : std::nullopt; if (error) { - controller()->show( - Ui::MakeInformBox(*error), - Ui::LayerOption::KeepOther); + controller()->showToast({ *error }); return false; } else if (!_peer || !_canSendMessages) { return false; @@ -6754,7 +6748,7 @@ void HistoryWidget::processReply() { return; } else if (_processingReplyItem->history() == _migrated) { if (_processingReplyItem->isService()) { - controller()->show(Ui::MakeInformBox(tr::lng_reply_cant())); + controller()->showToast({ tr::lng_reply_cant(tr::now) }); } else { const auto itemId = _processingReplyItem->fullId(); controller()->show( @@ -6777,13 +6771,13 @@ void HistoryWidget::processReply() { if (forum->topicDeleted(topicRootId)) { return processCancel(); } else if (const auto topic = forum->topicFor(topicRootId)) { - if (!topic->canWrite()) { + if (!Data::CanSendAnything(topic)) { return processCancel(); } } else { forum->requestTopic(topicRootId, processContinue()); } - } else if (!_peer->canWrite()) { + } else if (!Data::CanSendAnything(_peer)) { return processCancel(); } setReplyFieldsFromProcessing(); @@ -6849,8 +6843,7 @@ void HistoryWidget::editMessage(not_null item) { } else if (_chooseTheme) { toggleChooseChatTheme(_peer); } else if (_voiceRecordBar->isActive()) { - controller()->show( - Ui::MakeInformBox(tr::lng_edit_caption_voice())); + controller()->showToast({ tr::lng_edit_caption_voice(tr::now) }); return; } else if (_composeSearch) { _composeSearch->hideAnimated(); @@ -7264,9 +7257,11 @@ void HistoryWidget::handlePeerUpdate() { bool HistoryWidget::updateCanSendMessage() { const auto replyTo = (_replyToId && !_editMsgId) ? _replyEditMsg : 0; const auto topic = replyTo ? replyTo->topic() : nullptr; + const auto allWithoutPolls = Data::AllSendRestrictions() + & ~ChatRestriction::SendPolls; const auto newCanSendMessages = topic - ? topic->canWrite() - : _peer->canWrite(); + ? Data::CanSendAnyOf(topic, allWithoutPolls) + : Data::CanSendAnyOf(_peer, allWithoutPolls); if (_canSendMessages == newCanSendMessages) { return false; } @@ -7740,9 +7735,17 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { Painter p(this); const auto clip = e->rect(); if (_list) { - if (!_field->isHidden() || isRecording()) { + const auto restrictionHidden = !_field->isHidden() || isRecording(); + if (restrictionHidden + || replyToId() + || readyToForward() + || _kbShown) { drawField(p, clip); - } else if (const auto error = writeRestriction()) { + } + const auto error = restrictionHidden + ? std::nullopt + : writeRestriction(); + if (error) { drawRestrictedWrite(p, *error); } } else { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index f3281a6f0..67c81174a 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -430,6 +430,12 @@ private: Ui::PreparedList &&list, const QString &insertTextOnCancel = QString()); bool showSendingFilesError(const Ui::PreparedList &list) const; + bool showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const; + bool showSendMessageError( + const TextWithTags &textWithTags, + bool ignoreSlowmodeCountdown) const; void sendingFilesConfirmed( Ui::PreparedList &&list, diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index d37cf74a5..3c5e3e572 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -354,6 +354,7 @@ public: [[nodiscard]] bool isDisplayed() const; [[nodiscard]] bool isEditingMessage() const; [[nodiscard]] bool readyToForward() const; + [[nodiscard]] const HistoryItemsList &forwardItems() const; [[nodiscard]] FullMsgId replyingToMessage() const; [[nodiscard]] rpl::producer editMsgId() const; [[nodiscard]] rpl::producer scrollToItemRequests() const; @@ -839,6 +840,10 @@ bool FieldHeader::readyToForward() const { return !_forwardPanel->empty(); } +const HistoryItemsList &FieldHeader::forwardItems() const { + return _forwardPanel->items(); +} + FullMsgId FieldHeader::replyingToMessage() const { return _replyToId.current(); } @@ -1057,6 +1062,10 @@ int ComposeControls::heightCurrent() const { : _wrap->height(); } +const HistoryItemsList &ComposeControls::forwardItems() const { + return _header->forwardItems(); +} + bool ComposeControls::focus() { if (isRecording()) { return false; @@ -2061,12 +2070,12 @@ void ComposeControls::initSendButton() { void ComposeControls::initSendAsButton(not_null peer) { using namespace rpl::mappers; - // SendAsPeers::shouldChoose checks PeerData::canWrite(false). + // SendAsPeers::shouldChoose checks Data::CanSendAnything(PeerData*). rpl::combine( rpl::single(peer) | rpl::then( session().sendAsPeers().updated() | rpl::filter(_1 == peer) ), - Data::CanWriteValue(peer, false) + Data::CanSendAnythingValue(peer, false) ) | rpl::skip(1) | rpl::start_with_next([=] { if (updateSendAsButton()) { updateControlsVisibility(); @@ -2147,12 +2156,7 @@ void ComposeControls::initVoiceRecordBar() { if (!peer) { if (const auto error = Data::RestrictionError( peer, - ChatRestriction::SendMedia)) { - return error; - } - if (const auto error = Data::RestrictionError( - peer, - UserRestriction::SendVoiceMessages)) { + ChatRestriction::SendVoiceMessages)) { return error; } } @@ -2774,9 +2778,8 @@ bool ComposeControls::hasSilentBroadcastToggle() const { } const auto &peer = _history->peer; return peer - && peer->isChannel() - && !peer->isMegagroup() - && peer->canWrite() + && peer->isBroadcast() + && Data::CanSendAnything(peer) && !session().data().notifySettings().silentPostsUnknown(peer); } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index 903b11f75..25a92acfa 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -150,6 +150,7 @@ public: [[nodiscard]] bool isEditingMessage() const; [[nodiscard]] bool readyToForward() const; + [[nodiscard]] const HistoryItemsList &forwardItems() const; [[nodiscard]] FullMsgId replyingToMessage() const; [[nodiscard]] bool preventsClose(Fn &&continueCallback) const; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 91387b7b0..7ee998ee2 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -588,7 +588,9 @@ bool AddReplyToMessageAction( const auto peer = item ? item->history()->peer.get() : nullptr; if (!item || !item->isRegular() - || (topic ? !topic->canWrite() : !peer->canWrite()) + || !(topic + ? Data::CanSendAnything(topic) + : Data::CanSendAnything(peer)) || (context != Context::History && context != Context::Replies)) { return false; } diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 13812ade7..8605c960b 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2955,16 +2955,18 @@ bool Message::hasFastReply() const { } bool Message::displayFastReply() const { - const auto canWrite = [&] { + const auto canSendAnything = [&] { const auto item = data(); const auto peer = item->history()->peer; const auto topic = item->topic(); - return topic ? topic->canWrite() : peer->canWrite(); + return topic + ? Data::CanSendAnything(topic) + : Data::CanSendAnything(peer); }; return hasFastReply() && data()->isRegular() - && canWrite() + && canSendAnything() && !delegate()->elementInSelectionMode(); } diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 315be0e43..8db15853c 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_drag_area.h" #include "history/history_item_components.h" #include "history/history_item.h" +#include "history/history_item_helpers.h" // GetErrorTextForSending. #include "menu/menu_send.h" // SendMenu::Type. #include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_send_files_way.h" @@ -34,12 +35,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/wrap/slide_wrap.h" #include "ui/layers/generic_box.h" #include "ui/item_text_options.h" -#include "ui/toast/toast.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" #include "ui/effects/message_sending_animation_controller.h" #include "ui/ui_utility.h" -#include "ui/toasts/common_toasts.h" #include "base/timer_rpl.h" #include "api/api_bot.h" #include "api/api_common.h" @@ -687,18 +686,23 @@ void RepliesWidget::setupComposeControls() { session().changes().peerFlagsValue( _history->peer, Data::PeerUpdate::Flag::Rights), - Data::CanWriteValue(_history->peer), + Data::CanSendAnythingValue(_history->peer), std::move(topicWriteRestrictions) ) | rpl::map([=](auto, auto, std::optional topicRestriction) { + const auto allWithoutPolls = Data::AllSendRestrictions() + & ~ChatRestriction::SendPolls; + const auto canSendAnything = _topic + ? Data::CanSendAnyOf(_topic, allWithoutPolls) + : Data::CanSendAnyOf(_history->peer, allWithoutPolls); const auto restriction = Data::RestrictionError( _history->peer, - ChatRestriction::SendMessages); - return restriction - ? restriction + ChatRestriction::SendOther); + return !canSendAnything + ? (restriction + ? restriction + : tr::lng_group_not_accessible(tr::now)) : topicRestriction ? std::move(topicRestriction) - : !(_topic ? _topic->canWrite() : _history->peer->canWrite()) - ? tr::lng_group_not_accessible(tr::now) : std::optional(); }); @@ -841,7 +845,7 @@ void RepliesWidget::setupComposeControls() { channel->updateFull(); if (!channel->isBroadcast()) { rpl::combine( - Data::CanWriteValue(channel), + Data::CanSendAnythingValue(channel), channel->flagsValue() ) | rpl::start_with_next([=] { refreshJoinGroupButton(); @@ -855,13 +859,8 @@ void RepliesWidget::setupComposeControls() { void RepliesWidget::chooseAttach( std::optional overrideSendImagesAsPhotos) { _choosingAttach = false; - if (const auto error = Data::RestrictionError( - _history->peer, - ChatRestriction::SendMedia)) { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { *error }, - }); + if (const auto error = Data::AnyFileRestrictionError(_history->peer)) { + controller()->showToast({ *error }); return; } else if (showSlowmodeError()) { return; @@ -945,7 +944,8 @@ bool RepliesWidget::confirmSendingFiles( controller(), std::move(list), _composeControls->getTextWithAppliedMarkdown(), - _history->peer, + DefaultLimitsForPeer(_history->peer), + DefaultCheckForPeer(controller(), _history->peer), Api::SendType::Normal, SendMenu::Type::SilentOnly); // #TODO replies schedule @@ -980,7 +980,7 @@ void RepliesWidget::sendingFilesConfirmed( bool ctrlShiftEnter) { Expects(list.filesToProcess.empty()); - if (showSendingFilesError(list)) { + if (showSendingFilesError(list, way.sendImagesAsPhotos())) { return; } auto groups = DivideByGroups( @@ -1051,19 +1051,10 @@ bool RepliesWidget::showSlowmodeError() { if (text.isEmpty()) { return false; } - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { text }, - }); + controller()->showToast({ text }); return true; } -std::optional RepliesWidget::writeRestriction() const { - return Data::RestrictionError( - _history->peer, - ChatRestriction::SendMessages); -} - void RepliesWidget::pushReplyReturn(not_null item) { if (item->history() == _history && item->inThread(_rootId)) { _cornerButtons.pushReplyReturn(item); @@ -1095,16 +1086,17 @@ void RepliesWidget::uploadFile( bool RepliesWidget::showSendingFilesError( const Ui::PreparedList &list) const { + return showSendingFilesError(list, std::nullopt); +} + +bool RepliesWidget::showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const { const auto text = [&] { const auto peer = _history->peer; - const auto error = Data::RestrictionError( - peer, - ChatRestriction::SendMedia); + const auto error = Data::FileRestrictionError(peer, list, compress); if (error) { return *error; - } - if (peer->slowmodeApplied() && !list.canBeSentInSlowmode()) { - return tr::lng_slowmode_no_many(tr::now); } else if (const auto left = _history->peer->slowmodeSecondsLeft()) { return tr::lng_slowmode_enabled( tr::now, @@ -1132,10 +1124,7 @@ bool RepliesWidget::showSendingFilesError( return true; } - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { text }, - }); + controller()->showToast({ text }); return true; } @@ -1188,17 +1177,18 @@ void RepliesWidget::send(Api::SendOptions options) { message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); message.webPageId = webPageId; - //const auto error = GetErrorTextForSending( - // _peer, - // _toForward, - // message.textWithTags); - //if (!error.isEmpty()) { - // Ui::ShowMultilineToast({ - // .parentOverride = Window::Show(controller()).toastParent(), - // .text = { error }, - // }); - // return; - //} + const auto error = GetErrorTextForSending( + _history->peer, + { + .topicRootId = _topic ? _topic->rootId() : MsgId(0), + .forward = &_composeControls->forwardItems(), + .text = &message.textWithTags, + .ignoreSlowmodeCountdown = (options.scheduled != 0), + }); + if (!error.isEmpty()) { + controller()->showToast({ error }); + return; + } session().api().sendMessage(std::move(message)); @@ -1242,8 +1232,9 @@ void RepliesWidget::edit( return; } else if (!left.text.isEmpty()) { const auto remove = left.text.size(); - controller()->show(Ui::MakeInformBox( - tr::lng_edit_limit_reached(tr::now, lt_count, remove))); + controller()->showToast({ + tr::lng_edit_limit_reached(tr::now, lt_count, remove), + }); return; } @@ -1267,13 +1258,13 @@ void RepliesWidget::edit( } if (ranges::contains(Api::kDefaultEditMessagesErrors, error)) { - controller()->show(Ui::MakeInformBox(tr::lng_edit_error())); + controller()->showToast({ tr::lng_edit_error(tr::now) }); } else if (error == u"MESSAGE_NOT_MODIFIED"_q) { _composeControls->cancelEditMessage(); } else if (error == u"MESSAGE_EMPTY"_q) { doSetInnerFocus(); } else { - controller()->show(Ui::MakeInformBox(tr::lng_edit_error())); + controller()->showToast({ tr::lng_edit_error(tr::now) }); } update(); return true; @@ -1311,10 +1302,10 @@ void RepliesWidget::refreshJoinGroupButton() { } }; const auto channel = _history->peer->asChannel(); - const auto canWrite = !channel->isForum() - ? channel->canWrite() - : (_topic && _topic->canWrite()); - if (channel->amIn() || canWrite) { + const auto canSend = !channel->isForum() + ? Data::CanSendAnything(channel) + : (_topic && Data::CanSendAnything(_topic)); + if (channel->amIn() || canSend) { set(nullptr); } else { if (!_joinGroup) { @@ -1354,9 +1345,7 @@ bool RepliesWidget::sendExistingDocument( _history->peer, ChatRestriction::SendStickers); if (error) { - controller()->show( - Ui::MakeInformBox(*error), - Ui::LayerOption::KeepOther); + controller()->showToast({ *error }); return false; } else if (showSlowmodeError() || ShowSendPremiumError(controller(), document)) { @@ -1389,11 +1378,9 @@ bool RepliesWidget::sendExistingPhoto( Api::SendOptions options) { const auto error = Data::RestrictionError( _history->peer, - ChatRestriction::SendMedia); + ChatRestriction::SendPhotos); if (error) { - controller()->show( - Ui::MakeInformBox(*error), - Ui::LayerOption::KeepOther); + controller()->showToast({ *error }); return false; } else if (showSlowmodeError()) { return false; @@ -1413,7 +1400,7 @@ void RepliesWidget::sendInlineResult( not_null bot) { const auto errorText = result->getErrorOnSend(_history); if (!errorText.isEmpty()) { - controller()->show(Ui::MakeInformBox(errorText)); + controller()->showToast({ errorText }); return; } sendInlineResult(result, bot, {}, std::nullopt); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 0f8d2e0c1..cc7e71372 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -278,6 +278,9 @@ private: std::optional overrideSendImagesAsPhotos, const QString &insertTextOnCancel = QString()); bool showSendingFilesError(const Ui::PreparedList &list) const; + bool showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const; void sendingFilesConfirmed( Ui::PreparedList &&list, Ui::SendFilesWay way, @@ -307,7 +310,6 @@ private: void refreshJoinGroupButton(); [[nodiscard]] bool emptyShown() const; [[nodiscard]] bool showSlowmodeError(); - [[nodiscard]] std::optional writeRestriction() const; const not_null _history; MsgId _rootId = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index a0680c2db..ffe62daea 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -16,18 +16,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_drag_area.h" #include "history/history_item.h" +#include "history/history_item_helpers.h" // GetErrorTextForSending. #include "menu/menu_send.h" // SendMenu::Type. #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" #include "ui/layers/generic_box.h" #include "ui/item_text_options.h" -#include "ui/toast/toast.h" #include "ui/chat/chat_style.h" #include "ui/chat/attach/attach_prepare.h" #include "ui/chat/attach/attach_send_files_way.h" #include "ui/ui_utility.h" #include "ui/text/text_utilities.h" -#include "ui/toasts/common_toasts.h" #include "api/api_common.h" #include "api/api_editing.h" #include "api/api_sending.h" @@ -53,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_scheduled_messages.h" #include "data/data_user.h" #include "data/data_message_reactions.h" +#include "data/data_peer_values.h" #include "storage/storage_media_prepare.h" #include "storage/storage_account.h" #include "inline_bots/inline_bot_result.h" @@ -199,7 +199,30 @@ ScheduledWidget::ScheduledWidget( ScheduledWidget::~ScheduledWidget() = default; void ScheduledWidget::setupComposeControls() { - _composeControls->setHistory({ .history = _history.get() }); + auto writeRestriction = rpl::combine( + session().changes().peerFlagsValue( + _history->peer, + Data::PeerUpdate::Flag::Rights), + Data::CanSendAnythingValue(_history->peer) + ) | rpl::map([=] { + const auto allWithoutPolls = Data::AllSendRestrictions() + & ~ChatRestriction::SendPolls; + const auto canSendAnything = Data::CanSendAnyOf( + _history->peer, + allWithoutPolls); + const auto restriction = Data::RestrictionError( + _history->peer, + ChatRestriction::SendOther); + return !canSendAnything + ? (restriction + ? restriction + : tr::lng_group_not_accessible(tr::now)) + : std::optional(); + }); + _composeControls->setHistory({ + .history = _history.get(), + .writeRestriction = std::move(writeRestriction), + }); _composeControls->height( ) | rpl::start_with_next([=] { @@ -308,13 +331,8 @@ void ScheduledWidget::setupComposeControls() { } void ScheduledWidget::chooseAttach() { - if (const auto error = Data::RestrictionError( - _history->peer, - ChatRestriction::SendMedia)) { - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { *error }, - }); + if (const auto error = Data::AnyFileRestrictionError(_history->peer)) { + controller()->showToast({ *error }); return; } @@ -392,10 +410,11 @@ bool ScheduledWidget::confirmSendingFiles( controller(), std::move(list), _composeControls->getTextWithAppliedMarkdown(), - _history->peer, - CanScheduleUntilOnline(_history->peer) + DefaultLimitsForPeer(_history->peer), + DefaultCheckForPeer(controller(), _history->peer), + (CanScheduleUntilOnline(_history->peer) ? Api::SendType::ScheduledToUser - : Api::SendType::Scheduled, + : Api::SendType::Scheduled), SendMenu::Type::Disabled); box->setConfirmedCallback(crl::guard(this, [=]( @@ -429,7 +448,7 @@ void ScheduledWidget::sendingFilesConfirmed( bool ctrlShiftEnter) { Expects(list.filesToProcess.empty()); - if (showSendingFilesError(list)) { + if (showSendingFilesError(list, way.sendImagesAsPhotos())) { return; } auto groups = DivideByGroups(std::move(list), way, false); @@ -512,15 +531,19 @@ void ScheduledWidget::uploadFile( bool ScheduledWidget::showSendingFilesError( const Ui::PreparedList &list) const { + return showSendingFilesError(list, std::nullopt); +} + +bool ScheduledWidget::showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const { const auto text = [&] { - const auto error = Data::RestrictionError( - _history->peer, - ChatRestriction::SendMedia); + using Error = Ui::PreparedList::Error; + const auto peer = _history->peer; + const auto error = Data::FileRestrictionError(peer, list, compress); if (error) { return *error; - } - using Error = Ui::PreparedList::Error; - switch (list.error) { + } else switch (list.error) { case Error::None: return QString(); case Error::EmptyFile: case Error::Directory: @@ -540,10 +563,7 @@ bool ScheduledWidget::showSendingFilesError( return true; } - Ui::ShowMultilineToast({ - .parentOverride = Window::Show(controller()).toastParent(), - .text = { text }, - }); + controller()->showToast({ text }); return true; } @@ -555,7 +575,21 @@ Api::SendAction ScheduledWidget::prepareSendAction( } void ScheduledWidget::send() { - if (_composeControls->getTextWithAppliedMarkdown().text.isEmpty()) { + const auto textWithTags = _composeControls->getTextWithAppliedMarkdown(); + if (textWithTags.text.isEmpty()) { + return; + } + + const auto error = GetErrorTextForSending( + _history->peer, + { + .topicRootId = MsgId(), + .forward = nullptr, + .text = &textWithTags, + .ignoreSlowmodeCountdown = true, + }); + if (!error.isEmpty()) { + controller()->showToast({ error }); return; } const auto callback = [=](Api::SendOptions options) { send(options); }; @@ -571,18 +605,6 @@ void ScheduledWidget::send(Api::SendOptions options) { message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); message.webPageId = webPageId; - //const auto error = GetErrorTextForSending( - // _peer, - // _toForward, - // message.textWithTags); - //if (!error.isEmpty()) { - // Ui::ShowMultilineToast({ - // .parentOverride = Window::Show(controller()).toastParent(), - // .text = { error }, - // }); - // return; - //} - session().api().sendMessage(std::move(message)); _composeControls->clear(); @@ -647,8 +669,9 @@ void ScheduledWidget::edit( return; } else if (!left.text.isEmpty()) { const auto remove = left.text.size(); - controller()->show(Ui::MakeInformBox( - tr::lng_edit_limit_reached(tr::now, lt_count, remove))); + controller()->showToast({ + tr::lng_edit_limit_reached(tr::now, lt_count, remove) + }); return; } @@ -672,13 +695,13 @@ void ScheduledWidget::edit( } if (ranges::contains(Api::kDefaultEditMessagesErrors, error)) { - controller()->show(Ui::MakeInformBox(tr::lng_edit_error())); + controller()->showToast({ tr::lng_edit_error(tr::now) }); } else if (error == u"MESSAGE_NOT_MODIFIED"_q) { _composeControls->cancelEditMessage(); } else if (error == u"MESSAGE_EMPTY"_q) { _composeControls->focus(); } else { - controller()->show(Ui::MakeInformBox(tr::lng_edit_error())); + controller()->showToast({ tr::lng_edit_error(tr::now) }); } update(); return true; @@ -712,9 +735,7 @@ bool ScheduledWidget::sendExistingDocument( _history->peer, ChatRestriction::SendStickers); if (error) { - controller()->show( - Ui::MakeInformBox(*error), - Ui::LayerOption::KeepOther); + controller()->showToast({ *error }); return false; } else if (ShowSendPremiumError(controller(), document)) { return false; @@ -743,11 +764,9 @@ bool ScheduledWidget::sendExistingPhoto( Api::SendOptions options) { const auto error = Data::RestrictionError( _history->peer, - ChatRestriction::SendMedia); + ChatRestriction::SendPhotos); if (error) { - controller()->show( - Ui::MakeInformBox(*error), - Ui::LayerOption::KeepOther); + controller()->showToast({ *error }); return false; } @@ -765,7 +784,7 @@ void ScheduledWidget::sendInlineResult( not_null bot) { const auto errorText = result->getErrorOnSend(_history); if (!errorText.isEmpty()) { - controller()->show(Ui::MakeInformBox(errorText)); + controller()->showToast({ errorText }); return; } const auto callback = [=](Api::SendOptions options) { diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h index ec3d127f0..426cdf5fa 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h @@ -224,6 +224,9 @@ private: std::optional overrideSendImagesAsPhotos, const QString &insertTextOnCancel = QString()); bool showSendingFilesError(const Ui::PreparedList &list) const; + bool showSendingFilesError( + const Ui::PreparedList &list, + std::optional compress) const; void sendingFilesConfirmed( Ui::PreparedList &&list, Ui::SendFilesWay way, diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index 30878b49c..edcf5d785 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -1033,8 +1033,8 @@ void TopBarWidget::updateControlsVisibility() { const auto section = _activeChat.section; const auto historyMode = (section == Section::History); const auto hasPollsMenu = (_activeChat.key.peer() - && _activeChat.key.peer()->canSendPolls()) - || (topic && topic->canSendPolls()); + && Data::CanSend(_activeChat.key.peer(), ChatRestriction::SendPolls)) + || (topic && Data::CanSend(topic, ChatRestriction::SendPolls)); const auto hasTopicMenu = [&] { if (!topic || section != Section::Replies) { return false; diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index 579624c5c..5cb150426 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -145,7 +145,7 @@ void ShowChooseBox( }; auto filter = [=](not_null thread) -> bool { const auto peer = thread->peer(); - if (!thread->canWrite()) { + if (!Data::CanSend(thread, ChatRestriction::SendInline, false)) { return false; } else if (const auto user = peer->asUser()) { if (user->isBot()) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 40888f5b1..02de341e6 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_photo_media.h" #include "data/data_document_media.h" +#include "history/history.h" #include "history/history_item_reply_markup.h" #include "inline_bots/inline_bot_layout_item.h" #include "inline_bots/inline_bot_send_data.h" @@ -367,7 +368,7 @@ bool Result::hasThumbDisplay() const { }; void Result::addToHistory( - History *history, + not_null history, MessageFlags flags, MsgId msgId, PeerId fromId, @@ -394,8 +395,13 @@ void Result::addToHistory( std::move(markup)); } -QString Result::getErrorOnSend(History *history) const { - return sendData->getErrorOnSend(this, history); +QString Result::getErrorOnSend(not_null history) const { + const auto specific = sendData->getErrorOnSend(this, history); + return !specific.isEmpty() + ? specific + : Data::RestrictionError( + history->peer, + ChatRestriction::SendInline).value_or(QString()); } std::optional Result::getLocationPoint() const { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h index c92801bca..45c8f6b39 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h @@ -63,7 +63,7 @@ public: bool hasThumbDisplay() const; void addToHistory( - History *history, + not_null history, MessageFlags flags, MsgId msgId, PeerId fromId, @@ -71,7 +71,7 @@ public: UserId viaBotId, MsgId replyToId, const QString &postAuthor) const; - QString getErrorOnSend(History *history) const; + QString getErrorOnSend(not_null history) const; // interface for Layout:: usage std::optional getLocationPoint() const; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 76cb9cb27..c1ec877e8 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -59,10 +59,8 @@ void SendDataCommon::addToHistory( QString SendDataCommon::getErrorOnSend( const Result *owner, not_null history) const { - const auto error = Data::RestrictionError( - history->peer, - ChatRestriction::SendMessages); - return error.value_or(QString()); + const auto type = ChatRestriction::SendOther; + return Data::RestrictionError(history->peer, type).value_or(QString()); } SendDataCommon::SentMessageFields SendText::getSentMessageFields() const { @@ -140,10 +138,8 @@ void SendPhoto::addToHistory( QString SendPhoto::getErrorOnSend( const Result *owner, not_null history) const { - const auto error = Data::RestrictionError( - history->peer, - ChatRestriction::SendMedia); - return error.value_or(QString()); + const auto type = ChatRestriction::SendPhotos; + return Data::RestrictionError(history->peer, type).value_or(QString()); } void SendFile::addToHistory( @@ -173,24 +169,8 @@ void SendFile::addToHistory( QString SendFile::getErrorOnSend( const Result *owner, not_null history) const { - const auto errorMedia = Data::RestrictionError( - history->peer, - ChatRestriction::SendMedia); - const auto errorStickers = Data::RestrictionError( - history->peer, - ChatRestriction::SendStickers); - const auto errorGifs = Data::RestrictionError( - history->peer, - ChatRestriction::SendGifs); - return errorMedia - ? *errorMedia - : (errorStickers && (_document->sticker() != nullptr)) - ? *errorStickers - : (errorGifs - && _document->isAnimation() - && !_document->isVideoMessage()) - ? *errorGifs - : QString(); + const auto type = _document->requiredSendRight(); + return Data::RestrictionError(history->peer, type).value_or(QString()); } void SendGame::addToHistory( @@ -219,10 +199,8 @@ void SendGame::addToHistory( QString SendGame::getErrorOnSend( const Result *owner, not_null history) const { - const auto error = Data::RestrictionError( - history->peer, - ChatRestriction::SendGames); - return error.value_or(QString()); + const auto type = ChatRestriction::SendGames; + return Data::RestrictionError(history->peer, type).value_or(QString()); } SendDataCommon::SentMessageFields SendInvoice::getSentMessageFields() const { diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index 49339cd7e..17065d7ef 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -43,7 +43,7 @@ SendAsPeers::SendAsPeers(not_null session) bool SendAsPeers::shouldChoose(not_null peer) { refresh(peer); - return peer->canWrite(false) && (list(peer).size() > 1); + return Data::CanSendAnything(peer, false) && (list(peer).size() > 1); } void SendAsPeers::refresh(not_null peer, bool force) { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 7f83c7b7c..33788e077 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_media_types.h" #include "data/data_folder.h" +#include "data/data_forum_topic.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" @@ -363,9 +364,13 @@ MainWidget::MainWidget( const auto peer = key.peer(); const auto topic = key.topic(); auto canWrite = topic - ? Data::CanWriteValue(topic) + ? Data::CanSendAnyOfValue( + topic, + Data::TabbedPanelSendRestrictions()) : peer - ? Data::CanWriteValue(peer) + ? Data::CanSendAnyOfValue( + + peer, Data::TabbedPanelSendRestrictions()) : rpl::single(false); return std::move( canWrite @@ -559,7 +564,7 @@ bool MainWidget::setForwardDraft( .ignoreSlowmodeCountdown = true, }); if (!error.isEmpty()) { - Ui::show(Ui::MakeInformBox(error), Ui::LayerOption::KeepOther); + _controller->show(Ui::MakeInformBox(error)); return false; } @@ -575,7 +580,7 @@ bool MainWidget::shareUrl( not_null thread, const QString &url, const QString &text) const { - if (!thread->canWrite()) { + if (!Data::CanSendTexts(thread)) { _controller->show(Ui::MakeInformBox(tr::lng_share_cant())); return false; } @@ -606,8 +611,8 @@ bool MainWidget::shareUrl( bool MainWidget::inlineSwitchChosen( not_null thread, const QString &botAndQuery) const { - if (!thread->canWrite()) { - Ui::show(Ui::MakeInformBox(tr::lng_inline_switch_cant())); + if (!Data::CanSend(thread, ChatRestriction::SendInline)) { + _controller->show(Ui::MakeInformBox(tr::lng_inline_switch_cant())); return false; } const auto textWithTags = TextWithTags{ @@ -637,13 +642,13 @@ bool MainWidget::inlineSwitchChosen( bool MainWidget::sendPaths( not_null thread, const QStringList &paths) { - if (!thread->canWrite()) { - Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant())); + if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) { + _controller->show(Ui::MakeInformBox( + tr::lng_forward_send_files_cant())); return false; - } else if (const auto error = Data::RestrictionError( - thread->peer(), - ChatRestriction::SendMedia)) { - Ui::show(Ui::MakeInformBox(*error)); + } else if (const auto error = Data::AnyFileRestrictionError( + thread->peer())) { + _controller->show(Ui::MakeInformBox(*error)); return false; } else { _controller->showThread( @@ -685,8 +690,13 @@ bool MainWidget::filesOrForwardDrop( clearHider(_hider); } return false; - } else if (!thread->canWrite()) { - Ui::show(Ui::MakeInformBox(tr::lng_forward_send_files_cant())); + } else if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) { + _controller->show(Ui::MakeInformBox( + tr::lng_forward_send_files_cant())); + return false; + } else if (const auto error = Data::AnyFileRestrictionError( + thread->peer())) { + _controller->show(Ui::MakeInformBox(*error)); return false; } else { _controller->showThread( @@ -2111,7 +2121,8 @@ void MainWidget::hideAll() { void MainWidget::showAll() { if (cPasswordRecovered()) { cSetPasswordRecovered(false); - Ui::show(Ui::MakeInformBox(tr::lng_cloud_password_updated())); + _controller->show(Ui::MakeInformBox( + tr::lng_cloud_password_updated())); } if (isOneColumn()) { if (_sideShadow) { @@ -2720,7 +2731,7 @@ void MainWidget::activate() { _controller, paths[0].mid(interpret.size())); if (!error.isEmpty()) { - Ui::show(Ui::MakeInformBox(error)); + _controller->show(Ui::MakeInformBox(error)); } } else { const auto chosen = [=](not_null thread) { diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm index 515ea5941..0f851a03f 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm @@ -168,25 +168,22 @@ auto ActiveChat(not_null controller) { return Dialogs::Key(); } -bool CanWriteToActiveChat(not_null controller) { +bool CanSendToActiveChat( + not_null controller, + ChatRestriction right) { if (const auto topic = ActiveChat(controller).topic()) { - return topic->canWrite(); + return Data::CanSend(topic, right); } else if (const auto history = ActiveChat(controller).history()) { - return history->peer->canWrite(); + return Data::CanSend(history->peer, right); } return false; } -std::optional RestrictionToSendStickers(not_null peer) { - return Data::RestrictionError( - peer, - ChatRestriction::SendStickers); -} - -std::optional RestrictionToSendStickers( - not_null controller) { +std::optional RestrictionToSend( + not_null controller, + ChatRestriction right) { if (const auto peer = ActiveChat(controller).peer()) { - return RestrictionToSendStickers(peer); + return Data::RestrictionError(peer, right); } return std::nullopt; } @@ -364,10 +361,17 @@ void AppendEmojiPacks( gesture.allowableMovement = 0; [scrubber addGestureRecognizer:gesture]; - if (const auto error = RestrictionToSendStickers(_controller)) { + const auto kRight = ChatRestriction::SendStickers; + if (const auto error = RestrictionToSend(_controller, kRight)) { _error = std::make_unique( tr::lng_restricted_send_stickers_all(tr::now)); } + } else { + const auto kRight = ChatRestriction::SendOther; + if (const auto error = RestrictionToSend(_controller, kRight)) { + _error = std::make_unique( + tr::lng_restricted_send_message_all(tr::now)); + } } _lastPreviewedSticker = 0; @@ -467,16 +471,19 @@ void AppendEmojiPacks( - (void)scrubber:(NSScrubber*)scrubber didSelectItemAtIndex:(NSInteger)index { - if (!CanWriteToActiveChat(_controller) || _error) { - return; - } scrubber.selectedIndex = -1; const auto sticker = _itemsDataSource->at(index, _type); const auto document = sticker.document; const auto emoji = sticker.emoji; + const auto kRight = document + ? ChatRestriction::SendStickers + : ChatRestriction::SendOther; + if (!CanSendToActiveChat(_controller, kRight) || _error) { + return; + } auto callback = [=] { if (document) { - if (const auto error = RestrictionToSendStickers(_controller)) { + if (const auto error = RestrictionToSend(_controller, kRight)) { _controller->show(Ui::MakeInformBox(*error)); return true; } else if (Window::ShowSendPremiumError(_controller->sessionController(), document)) { @@ -488,7 +495,10 @@ void AppendEmojiPacks( document); return true; } else if (emoji) { - if (const auto inputField = qobject_cast( + if (const auto error = RestrictionToSend(_controller, kRight)) { + _controller->show(Ui::MakeInformBox(*error)); + return true; + } else if (const auto inputField = qobject_cast( QApplication::focusWidget())) { Ui::InsertEmojiAtCursor(inputField->textCursor(), emoji); Core::App().settings().incrementRecentEmoji({ emoji }); @@ -562,9 +572,11 @@ void AppendEmojiPacks( ) | rpl::map([](Dialogs::Key k) { const auto topic = k.topic(); const auto peer = k.peer(); + const auto right = ChatRestriction::SendStickers; return peer - && !RestrictionToSendStickers(peer) - && (topic ? topic->canWrite() : peer->canWrite()); + && (topic + ? Data::CanSend(topic, right) + : Data::CanSend(peer, right)); }) | rpl::distinct_until_changed( ) | rpl::start_with_next([=](bool value) { [self dismissPopover:nil]; diff --git a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm index f6b8da35a..555e98b32 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/mac_touchbar_manager.mm @@ -9,8 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" // ApiWrap::updateStickers() #include "core/application.h" -#include "data/data_peer.h" // PeerData::canWrite() -#include "data/data_forum_topic.h" // Data::ForumTopic::canWrite() +#include "data/data_chat_participant_status.h" // Data::CanSendAnyOf. #include "data/data_session.h" #include "data/stickers/data_stickers.h" // Stickers::setsRef() #include "main/main_domain.h" @@ -146,9 +145,11 @@ const auto kAudioItemIdentifier = @"touchbarAudio"; ) | rpl::map([](Dialogs::Key k) { const auto topic = k.topic(); const auto peer = k.peer(); + const auto rights = ChatRestriction::SendStickers + | ChatRestriction::SendOther; return topic - ? topic->canWrite() - : (peer && peer->canWrite()); + ? Data::CanSendAnyOf(topic, rights) + : (peer && Data::CanSendAnyOf(peer, rights)); }) | rpl::distinct_until_changed() ) | rpl::start_with_next([=]( bool canApplyMarkdown, diff --git a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp index af8f1751c..6ca3bd241 100644 --- a/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp +++ b/Telegram/SourceFiles/ui/chat/attach/attach_prepare.cpp @@ -35,6 +35,22 @@ bool PreparedFile::canBeInAlbumType(AlbumType album) const { return CanBeInAlbumType(type, album); } +bool PreparedFile::isSticker() const { + Expects(information != nullptr); + + return (type == PreparedFile::Type::Photo) + && Core::IsMimeSticker(information->filemime); +} + +bool PreparedFile::isGifv() const { + Expects(information != nullptr); + + using Video = Ui::PreparedFileInformation::Video; + return (type == PreparedFile::Type::Video) + && v::is