From 94c2969f8bfab1009b1707264f87d4c45fefa577 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 30 Sep 2020 14:06:08 +0300 Subject: [PATCH] Support slowmode restrictions in Replies section. --- Telegram/SourceFiles/data/data_peer.cpp | 25 +++++ Telegram/SourceFiles/data/data_peer.h | 1 + .../SourceFiles/history/history_widget.cpp | 9 +- .../view/history_view_compose_controls.cpp | 44 +++++---- .../view/history_view_compose_controls.h | 13 ++- .../history/view/history_view_message.cpp | 2 + .../view/history_view_replies_section.cpp | 97 ++++++++++++++++++- .../view/history_view_scheduled_section.cpp | 2 +- .../window/window_session_controller.cpp | 4 +- Telegram/lib_base | 2 +- Telegram/lib_rpl | 2 +- 11 files changed, 174 insertions(+), 27 deletions(-) diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index ca82357b95..59e41781cf 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -836,6 +836,31 @@ bool PeerData::slowmodeApplied() const { return false; } +rpl::producer PeerData::slowmodeAppliedValue() const { + using namespace rpl::mappers; + const auto channel = asChannel(); + if (!channel) { + return rpl::single(false); + } + + auto hasAdminRights = channel->adminRightsValue( + ) | rpl::map([=] { + return channel->hasAdminRights(); + }) | rpl::distinct_until_changed(); + + auto slowmodeEnabled = channel->flagsValue( + ) | rpl::filter([=](const ChannelData::Flags::Change &change) { + return (change.diff & MTPDchannel::Flag::f_slowmode_enabled) != 0; + }) | rpl::map([=](const ChannelData::Flags::Change &change) { + return (change.value & MTPDchannel::Flag::f_slowmode_enabled) != 0; + }) | rpl::distinct_until_changed(); + + return rpl::combine( + std::move(hasAdminRights), + std::move(slowmodeEnabled), + !_1 && _2); +} + int PeerData::slowmodeSecondsLeft() const { if (const auto channel = asChannel()) { if (const auto seconds = channel->slowmodeSeconds()) { diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index cd935e9e00..3712adca93 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -199,6 +199,7 @@ public: [[nodiscard]] bool amAnonymous() const; [[nodiscard]] bool canRevokeFullHistory() const; [[nodiscard]] bool slowmodeApplied() const; + [[nodiscard]] rpl::producer slowmodeAppliedValue() const; [[nodiscard]] int slowmodeSecondsLeft() const; [[nodiscard]] bool canSendPolls() const; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 7154ebe67e..20c36f6ef9 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3675,15 +3675,18 @@ void HistoryWidget::updateSendButtonType() { const auto type = computeSendButtonType(); _send->setType(type); + // This logic is duplicated in RepliesWidget. + const auto disabledBySlowmode = _peer + && _peer->slowmodeApplied() + && (_history->latestSendingMessage() != nullptr); + const auto delay = [&] { return (type != Type::Cancel && type != Type::Save && _peer) ? _peer->slowmodeSecondsLeft() : 0; }(); _send->setSlowmodeDelay(delay); - _send->setDisabled(_peer - && _peer->slowmodeApplied() - && (_history->latestSendingMessage() != nullptr) + _send->setDisabled(disabledBySlowmode && (type == Type::Send || type == Type::Record)); if (delay != 0) { diff --git a/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp index a0eedc02c2..52080bfaad 100644 --- a/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/history_view_compose_controls.cpp @@ -528,7 +528,13 @@ Main::Session &ComposeControls::session() const { return _window->session(); } -void ComposeControls::setHistory(History *history) { +void ComposeControls::setHistory(SetHistoryArgs &&args) { + _showSlowmodeError = std::move(args.showSlowmodeError); + _slowmodeSecondsLeft = rpl::single(0) + | rpl::then(std::move(args.slowmodeSecondsLeft)); + _sendDisabledBySlowmode = rpl::single(false) + | rpl::then(std::move(args.sendDisabledBySlowmode)); + const auto history = *args.history; if (_history == history) { return; } @@ -878,8 +884,8 @@ void ComposeControls::recordStartCallback() { if (error) { Ui::show(Box(*error)); return; - //} else if (showSlowmodeError()) { // #TODO slowmode - // return; + } else if (_showSlowmodeError && _showSlowmodeError()) { + return; } else if (!::Media::Capture::instance()->available()) { return; } @@ -1034,7 +1040,13 @@ void ComposeControls::initTabbedSelector() { } void ComposeControls::initSendButton() { - updateSendButtonType(); + rpl::combine( + _slowmodeSecondsLeft.value(), + _sendDisabledBySlowmode.value() + ) | rpl::start_with_next([=] { + updateSendButtonType(); + }, _send->lifetime()); + _send->finishAnimating(); } @@ -1053,22 +1065,13 @@ void ComposeControls::updateSendButtonType() { _send->setType(type); const auto delay = [&] { - return /*(type != Type::Cancel && type != Type::Save && _peer) - ? _peer->slowmodeSecondsLeft() - : */0; + return (type != Type::Cancel && type != Type::Save) + ? _slowmodeSecondsLeft.current() + : 0; }(); _send->setSlowmodeDelay(delay); - //_send->setDisabled(_peer - // && _peer->slowmodeApplied() - // && (_history->latestSendingMessage() != nullptr) - // && (type == Type::Send || type == Type::Record)); - - //if (delay != 0) { - // base::call_delayed( - // kRefreshSlowmodeLabelTimeout, - // this, - // [=] { updateSendButtonType(); }); - //} + _send->setDisabled(_sendDisabledBySlowmode.current() + && (type == Type::Send || type == Type::Record)); _send->setRecordStartCallback([=] { recordStartCallback(); }); _send->setRecordStopCallback([=](bool active) { recordStopCallback(active); }); @@ -1076,6 +1079,11 @@ void ComposeControls::updateSendButtonType() { _send->setRecordAnimationCallback([=] { _wrap->update(); }); } +void ComposeControls::finishAnimating() { + _send->finishAnimating(); + _recordingAnimation.stop(); +} + void ComposeControls::updateControlsGeometry(QSize size) { // _attachToggle -- _inlineResults ------ _tabbedPanel -- _fieldBarCancel // (_attachDocument|_attachPhoto) _field _tabbedSelectorToggle _send diff --git a/Telegram/SourceFiles/history/view/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/history_view_compose_controls.h index 2fd4214df4..61c4ce1f53 100644 --- a/Telegram/SourceFiles/history/view/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/history_view_compose_controls.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/required.h" #include "api/api_common.h" #include "base/unique_qptr.h" #include "ui/rp_widget.h" @@ -88,7 +89,14 @@ public: [[nodiscard]] Main::Session &session() const; - void setHistory(History *history); + struct SetHistoryArgs { + required history; + Fn showSlowmodeError; + rpl::producer slowmodeSecondsLeft; + rpl::producer sendDisabledBySlowmode; + }; + void setHistory(SetHistoryArgs &&args); + void finishAnimating(); void move(int x, int y); void resizeToWidth(int width); @@ -184,6 +192,9 @@ private: const not_null _parent; const not_null _window; History *_history = nullptr; + Fn _showSlowmodeError; + rpl::variable _slowmodeSecondsLeft; + rpl::variable _sendDisabledBySlowmode; Mode _mode = Mode::Normal; const std::unique_ptr _wrap; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 82f7bc1045..03454dfbe5 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -746,6 +746,8 @@ void Message::paintCommentsButton( QImage::Format_ARGB32_Premultiplied); } _comments->cachedUserpics.fill(Qt::transparent); + _comments->cachedUserpics.setDevicePixelRatio(cRetinaFactor()); + auto q = Painter(&_comments->cachedUserpics); auto hq = PainterHighQualityEnabler(q); auto pen = QPen(Qt::transparent); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 23d478b603..d6c347edb9 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -22,9 +22,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/layers/generic_box.h" #include "ui/text_options.h" #include "ui/toast/toast.h" +#include "ui/text/format_values.h" #include "ui/special_buttons.h" #include "ui/ui_utility.h" #include "ui/toasts/common_toasts.h" +#include "base/timer_rpl.h" #include "api/api_common.h" #include "api/api_editing.h" #include "api/api_sending.h" @@ -62,6 +64,7 @@ namespace HistoryView { namespace { constexpr auto kReadRequestTimeout = 3 * crl::time(1000); +constexpr auto kRefreshSlowmodeLabelTimeout = crl::time(200); bool CanSendFiles(not_null data) { if (data->hasImage()) { @@ -135,6 +138,8 @@ RepliesWidget::RepliesWidget( setupRoot(); setupRootView(); + session().api().requestFullPeer(_history->peer); + _topBar->setActiveChat( _history, TopBarWidget::Section::Replies, @@ -396,7 +401,51 @@ bool RepliesWidget::computeAreComments() const { } void RepliesWidget::setupComposeControls() { - _composeControls->setHistory(_history); + auto slowmodeSecondsLeft = session().changes().peerFlagsValue( + _history->peer, + Data::PeerUpdate::Flag::Slowmode + ) | rpl::map([=] { + return _history->peer->slowmodeSecondsLeft(); + }) | rpl::map([=](int delay) -> rpl::producer { + auto start = rpl::single(delay); + if (!delay) { + return start; + } + return std::move( + start + ) | rpl::then(base::timer_each( + kRefreshSlowmodeLabelTimeout + ) | rpl::map([=] { + return _history->peer->slowmodeSecondsLeft(); + }) | rpl::take_while([=](int delay) { + return delay > 0; + })) | rpl::then(rpl::single(0)); + }) | rpl::flatten_latest(); + + const auto channel = _history->peer->asChannel(); + Assert(channel != nullptr); + + auto hasSendingMessage = session().changes().historyFlagsValue( + _history, + Data::HistoryUpdate::Flag::LocalMessages + ) | rpl::map([=] { + return _history->latestSendingMessage() != nullptr; + }) | rpl::distinct_until_changed(); + + using namespace rpl::mappers; + auto sendDisabledBySlowmode = (!channel || channel->amCreator()) + ? (rpl::single(false) | rpl::type_erased()) + : rpl::combine( + channel->slowmodeAppliedValue(), + std::move(hasSendingMessage), + _1 && _2); + + _composeControls->setHistory({ + .history = _history.get(), + .showSlowmodeError = [=] { return showSlowmodeError(); }, + .slowmodeSecondsLeft = std::move(slowmodeSecondsLeft), + .sendDisabledBySlowmode = std::move(sendDisabledBySlowmode), + }); _composeControls->height( ) | rpl::start_with_next([=] { @@ -497,6 +546,8 @@ void RepliesWidget::setupComposeControls() { } Unexpected("action in MimeData hook."); }); + + _composeControls->finishAnimating(); } void RepliesWidget::chooseAttach() { @@ -507,6 +558,8 @@ void RepliesWidget::chooseAttach() { .text = { *error }, }); return; + } else if (showSlowmodeError()) { + return; } const auto filter = FileDialog::AllFilesFilter() @@ -684,6 +737,30 @@ bool RepliesWidget::confirmSendingFiles( insertTextOnCancel); } +bool RepliesWidget::showSlowmodeError() { + const auto text = [&] { + if (const auto left = _history->peer->slowmodeSecondsLeft()) { + return tr::lng_slowmode_enabled( + tr::now, + lt_left, + Ui::FormatDurationWords(left)); + } else if (_history->peer->slowmodeApplied()) { + if (const auto item = _history->latestSendingMessage()) { + showAtPositionNow(item->position(), nullptr); + return tr::lng_slowmode_no_many(tr::now); + } + } + return QString(); + }(); + if (text.isEmpty()) { + return false; + } + Ui::ShowMultilineToast({ + .text = { text }, + }); + return true; +} + void RepliesWidget::uploadFilesAfterConfirmation( Storage::PreparedList &&list, SendMediaType type, @@ -783,6 +860,16 @@ bool RepliesWidget::showSendingFilesError( if (error) { return *error; } + if (list.files.size() > 1 + && _history->peer->slowmodeApplied() + && !list.albumIsPossible) { + return tr::lng_slowmode_no_many(tr::now); + } else if (const auto left = _history->peer->slowmodeSecondsLeft()) { + return tr::lng_slowmode_enabled( + tr::now, + lt_left, + Ui::FormatDurationWords(left)); + } using Error = Storage::PreparedList::Error; switch (list.error) { case Error::None: return QString(); @@ -831,6 +918,10 @@ void RepliesWidget::sendVoice( } void RepliesWidget::send(Api::SendOptions options) { + if (!options.scheduled && showSlowmodeError()) { + return; + } + const auto webPageId = _composeControls->webPageId();/* _previewCancelled ? CancelledWebPageId : ((_previewData && _previewData->pendingTill >= 0) @@ -964,6 +1055,8 @@ bool RepliesWidget::sendExistingDocument( if (error) { Ui::show(Box(*error), Ui::LayerOption::KeepOther); return false; + } else if (showSlowmodeError()) { + return false; } auto message = Api::MessageToSend(_history); @@ -1004,6 +1097,8 @@ bool RepliesWidget::sendExistingPhoto( if (error) { Ui::show(Box(*error), Ui::LayerOption::KeepOther); return false; + } else if (showSlowmodeError()) { + return false; } auto message = Api::MessageToSend(_history); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index a1327cab68..4e7e2a8681 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -152,7 +152,7 @@ ScheduledWidget::ScheduledWidget( ScheduledWidget::~ScheduledWidget() = default; void ScheduledWidget::setupComposeControls() { - _composeControls->setHistory(_history); + _composeControls->setHistory({ .history = _history }); _composeControls->height( ) | rpl::start_with_next([=] { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 43752613d5..57028d65ec 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -234,7 +234,6 @@ void SessionNavigation::showPeerByLinkResolved( showPeerHistory(peer->id, params, msgId); }); } - } void SessionNavigation::showRepliesForMessage( @@ -246,6 +245,9 @@ void SessionNavigation::showRepliesForMessage( && _showingRepliesHistory == history.get() && _showingRepliesRootId == rootId) { return; + } else if (!history->peer->asChannel()) { + // HistoryView::RepliesWidget right now handles only channels. + return; } _session->api().request(base::take(_showingRepliesRequestId)).cancel(); diff --git a/Telegram/lib_base b/Telegram/lib_base index c28f55ab25..4f03dbd9a0 160000 --- a/Telegram/lib_base +++ b/Telegram/lib_base @@ -1 +1 @@ -Subproject commit c28f55ab25c9ee7aee7e960c6a4127b008742bdd +Subproject commit 4f03dbd9a0c3dce0268fa208bf1e3ffff4c95c72 diff --git a/Telegram/lib_rpl b/Telegram/lib_rpl index e654c5ee98..86b9b80fae 160000 --- a/Telegram/lib_rpl +++ b/Telegram/lib_rpl @@ -1 +1 @@ -Subproject commit e654c5ee98199b47a426aaaf6a1f7331aca0ebcd +Subproject commit 86b9b80fae7a41e7c6ad85f14659883533ab84e8