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 61a308db1c..eadde1509c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" +#include "chat_helpers/send_context_menu.h" #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_section.h" #include "chat_helpers/tabbed_selector.h" @@ -34,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/view/controls/history_view_voice_record_bar.h" +#include "history/view/history_view_schedule_box.h" // HistoryView::PrepareScheduleBox #include "history/view/history_view_webpage_preview.h" #include "inline_bots/inline_results_widget.h" #include "inline_bots/inline_bot_result.h" @@ -575,7 +577,8 @@ MessageToEdit FieldHeader::queryToEdit() { ComposeControls::ComposeControls( not_null parent, not_null window, - Mode mode) + Mode mode, + SendMenu::Type sendMenuType) : _parent(parent) , _window(window) , _mode(mode) @@ -609,6 +612,7 @@ ComposeControls::ComposeControls( window, _send, st::historySendSize.height())) +, _sendMenuType(sendMenuType) , _saveDraftTimer([=] { saveDraft(); }) { init(); } @@ -717,19 +721,21 @@ rpl::producer> ComposeControls::keyEvents() const { }); } -rpl::producer<> ComposeControls::sendRequests() const { +rpl::producer ComposeControls::sendRequests() const { auto filter = rpl::filter([=] { const auto type = (_mode == Mode::Normal) ? Ui::SendButton::Type::Send : Ui::SendButton::Type::Schedule; return (_send->type() == type); }); + auto map = rpl::map_to(Api::SendOptions()); auto submits = base::qt_signal_producer( _field.get(), &Ui::InputField::submitted); return rpl::merge( - _send->clicks() | filter | rpl::to_empty, - std::move(submits) | filter | rpl::to_empty); + _send->clicks() | filter | map, + std::move(submits) | filter | map, + _sendCustomRequests.events()); } rpl::producer ComposeControls::sendVoiceRequests() const { @@ -1395,6 +1401,12 @@ void ComposeControls::initSendButton() { }) | rpl::start_with_next([=] { cancelInlineBot(); }, _send->lifetime()); + + SendMenu::SetupMenuAndShortcuts( + _send.get(), + [=] { return sendButtonMenuType(); }, + [=] { sendSilent(); }, + [=] { sendScheduled(); }); } void ComposeControls::inlineBotResolveDone( @@ -1550,18 +1562,32 @@ void ComposeControls::updateWrappingVisibility() { } } +auto ComposeControls::computeSendButtonType() const { + using Type = Ui::SendButton::Type; + + if (_header->isEditingMessage()) { + return Type::Save; + } else if (_isInlineBot) { + return Type::Cancel; + } else if (showRecordButton()) { + return Type::Record; + } + return (_mode == Mode::Normal) ? Type::Send : Type::Schedule; +} + +SendMenu::Type ComposeControls::sendMenuType() const { + return !_history ? SendMenu::Type::Disabled : _sendMenuType; +} + +SendMenu::Type ComposeControls::sendButtonMenuType() const { + return (computeSendButtonType() == Ui::SendButton::Type::Send) + ? sendMenuType() + : SendMenu::Type::Disabled; +} + void ComposeControls::updateSendButtonType() { using Type = Ui::SendButton::Type; - const auto type = [&] { - if (_header->isEditingMessage()) { - return Type::Save; - } else if (_isInlineBot) { - return Type::Cancel; - } else if (showRecordButton()) { - return Type::Record; - } - return (_mode == Mode::Normal) ? Type::Send : Type::Schedule; - }(); + const auto type = computeSendButtonType(); _send->setType(type); const auto delay = [&] { @@ -2076,6 +2102,22 @@ bool ComposeControls::isRecording() const { return _voiceRecordBar->isRecording(); } +void ComposeControls::sendSilent() { + _sendCustomRequests.fire({ .silent = true }); +} + +void ComposeControls::sendScheduled() { + auto callback = [=](Api::SendOptions options) { + _sendCustomRequests.fire(std::move(options)); + }; + Ui::show( + HistoryView::PrepareScheduleBox( + _wrap.get(), + sendMenuType(), + std::move(callback)), + Ui::LayerOption::KeepOther); +} + void ComposeControls::updateInlineBotQuery() { if (!_history) { return; 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 109a318a06..fbdce2296c 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -21,6 +21,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class History; class FieldAutocomplete; +namespace SendMenu { +enum class Type; +} // namespace SendMenu + namespace ChatHelpers { class TabbedPanel; class TabbedSelector; @@ -87,7 +91,8 @@ public: ComposeControls( not_null parent, not_null window, - Mode mode); + Mode mode, + SendMenu::Type sendMenuType); ~ComposeControls(); [[nodiscard]] Main::Session &session() const; @@ -104,7 +109,7 @@ public: bool focus(); [[nodiscard]] rpl::producer<> cancelRequests() const; - [[nodiscard]] rpl::producer<> sendRequests() const; + [[nodiscard]] rpl::producer sendRequests() const; [[nodiscard]] rpl::producer sendVoiceRequests() const; [[nodiscard]] rpl::producer sendCommandRequests() const; [[nodiscard]] rpl::producer editRequests() const; @@ -184,6 +189,13 @@ private: void updateOuterGeometry(QRect rect); void paintBackground(QRect clip); + [[nodiscard]] auto computeSendButtonType() const; + [[nodiscard]] SendMenu::Type sendMenuType() const; + [[nodiscard]] SendMenu::Type sendButtonMenuType() const; + + void sendSilent(); + void sendScheduled(); + void orderControls(); void checkAutocomplete(); void updateStickersByEmoji(); @@ -258,6 +270,9 @@ private: const std::unique_ptr _header; const std::unique_ptr _voiceRecordBar; + const SendMenu::Type _sendMenuType; + + rpl::event_stream _sendCustomRequests; rpl::event_stream<> _cancelRequests; rpl::event_stream _fileChosen; rpl::event_stream _photoChosen; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index e5a976a574..4be479126c 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -158,7 +158,8 @@ RepliesWidget::RepliesWidget( , _composeControls(std::make_unique( this, controller, - ComposeControls::Mode::Normal)) + ComposeControls::Mode::Normal, + SendMenu::Type::SilentOnly)) , _scroll(std::make_unique(this, st::historyScroll, false)) , _scrollDown(_scroll.get(), st::historyToDown) , _readRequestTimer([=] { sendReadTillRequest(); }) { @@ -429,13 +430,13 @@ void RepliesWidget::setupComposeControls() { }, lifetime()); _composeControls->sendRequests( - ) | rpl::start_with_next([=] { - send(); + ) | rpl::start_with_next([=](Api::SendOptions options) { + send(options); }, lifetime()); _composeControls->sendVoiceRequests( ) | rpl::start_with_next([=](ComposeControls::VoiceToSend &&data) { - sendVoice(data.bytes, data.waveform, data.duration); + sendVoice(std::move(data)); }, lifetime()); _composeControls->sendCommandRequests( @@ -878,13 +879,15 @@ void RepliesWidget::send() { // Ui::LayerOption::KeepOther); } -void RepliesWidget::sendVoice( - QByteArray bytes, - VoiceWaveform waveform, - int duration) { +void RepliesWidget::sendVoice(ComposeControls::VoiceToSend &&data) { auto action = Api::SendAction(_history); action.replyTo = replyToId(); - session().api().sendVoiceMessage(bytes, waveform, duration, action); + action.options = data.options; + session().api().sendVoiceMessage( + data.bytes, + data.waveform, + data.duration, + std::move(action)); } void RepliesWidget::send(Api::SendOptions options) { diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.h b/Telegram/SourceFiles/history/view/history_view_replies_section.h index 66e8b7eb06..93ea4fbb10 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.h +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.h @@ -52,6 +52,10 @@ class RepliesList; namespace HistoryView { +namespace Controls { +struct VoiceToSend; +} // namespace Controls + class Element; class TopBarWidget; class RepliesMemento; @@ -180,7 +184,7 @@ private: void send(); void send(Api::SendOptions options); - void sendVoice(QByteArray bytes, VoiceWaveform waveform, int duration); + void sendVoice(Controls::VoiceToSend &&data); void edit( not_null item, Api::SendOptions options, diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 4e09b10401..8fca948b2d 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -97,7 +97,8 @@ ScheduledWidget::ScheduledWidget( , _composeControls(std::make_unique( this, controller, - ComposeControls::Mode::Scheduled)) + ComposeControls::Mode::Scheduled, + SendMenu::Type::Disabled)) , _scrollDown(_scroll, st::historyToDown) { const auto state = Dialogs::EntryState{ .key = _history,