From 4691cff3f604a4a99ba97a26d56f7649c38b36f7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 16:10:51 +0400 Subject: [PATCH] Start SendAsButton in HistoryWidget. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/data/data_peer_values.cpp | 50 ++++++++++++ Telegram/SourceFiles/data/data_peer_values.h | 10 +++ .../SourceFiles/history/history_widget.cpp | 63 ++++++++++++++- Telegram/SourceFiles/history/history_widget.h | 4 + Telegram/SourceFiles/main/main_session.cpp | 2 + Telegram/SourceFiles/main/main_session.h | 5 ++ .../main/session/send_as_peers.cpp | 78 +++++++++++++++++++ .../SourceFiles/main/session/send_as_peers.h | 40 ++++++++++ Telegram/SourceFiles/ui/chat/chat.style | 25 ++++++ .../ui/controls/send_as_button.cpp | 70 +++++++++++++++++ .../SourceFiles/ui/controls/send_as_button.h | 39 ++++++++++ .../SourceFiles/ui/controls/send_button.h | 2 +- Telegram/cmake/td_ui.cmake | 2 + Telegram/lib_rpl | 2 +- 15 files changed, 390 insertions(+), 4 deletions(-) create mode 100644 Telegram/SourceFiles/main/session/send_as_peers.cpp create mode 100644 Telegram/SourceFiles/main/session/send_as_peers.h create mode 100644 Telegram/SourceFiles/ui/controls/send_as_button.cpp create mode 100644 Telegram/SourceFiles/ui/controls/send_as_button.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index dc71724c2..9c67ebcfb 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -785,6 +785,8 @@ PRIVATE main/main_session.h main/main_session_settings.cpp main/main_session_settings.h + main/session/send_as_peers.cpp + main/session/send_as_peers.h media/system_media_controls_manager.h media/system_media_controls_manager.cpp media/audio/media_audio.cpp diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 110afa755..668cad623 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -11,6 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" +#include "data/data_changes.h" +#include "main/main_session.h" +#include "ui/image/image_prepare.h" #include "base/unixtime.h" namespace Data { @@ -445,4 +448,51 @@ bool ChannelHasActiveCall(not_null channel) { return (channel->flags() & ChannelDataFlag::CallNotEmpty); } +rpl::producer PeerUserpicImageValue( + not_null peer, + int size) { + return PeerUserpicImageValue(peer, size, ImageRoundRadius::Ellipse); +} + +rpl::producer PeerUserpicImageValue( + not_null peer, + int size, + ImageRoundRadius radius) { + return [=](auto consumer) { + auto result = rpl::lifetime(); + struct State { + std::shared_ptr view; + rpl::lifetime waiting; + InMemoryKey key = {}; + bool empty = true; + Fn push; + }; + const auto state = result.make_state(); + state->push = [=] { + const auto key = peer->userpicUniqueKey(state->view); + const auto loading = !state->view || state->view->image(); + + if (loading && !state->waiting) { + peer->session().downloaderTaskFinished( + ) | rpl::start_with_next(state->push, state->waiting); + } else if (!loading && state->waiting) { + state->waiting.destroy(); + } + + if (!state->empty && (loading || key == state->key)) { + return; + } + state->key = key; + state->empty = false; + consumer.put_next( + peer->generateUserpicImage(state->view, size, radius)); + }; + peer->session().changes().peerFlagsValue( + peer, + PeerUpdate::Flag::Photo + ) | rpl::start_with_next(state->push, result); + return result; + }; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_peer_values.h b/Telegram/SourceFiles/data/data_peer_values.h index b371d242c..31cbcb6b3 100644 --- a/Telegram/SourceFiles/data/data_peer_values.h +++ b/Telegram/SourceFiles/data/data_peer_values.h @@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include "data/data_peer.h" +enum class ImageRoundRadius; + namespace Data { template @@ -110,4 +112,12 @@ inline auto PeerFullFlagValue( [[nodiscard]] bool IsUserOnline(not_null user); [[nodiscard]] bool ChannelHasActiveCall(not_null channel); +[[nodiscard]] rpl::producer PeerUserpicImageValue( + not_null peer, + int size); +[[nodiscard]] rpl::producer PeerUserpicImageValue( + not_null peer, + int size, + ImageRoundRadius radius); + } // namespace Data diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 292804630..ba56c4573 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/special_buttons.h" #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" +#include "ui/controls/send_as_button.h" #include "inline_bots/inline_bot_result.h" #include "base/event_filter.h" #include "base/qt_signal_producer.h" @@ -52,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_web_page.h" #include "data/data_document.h" #include "data/data_photo.h" +#include "data/data_peer_values.h" #include "data/data_media_types.h" #include "data/data_channel.h" #include "data/data_chat.h" @@ -120,6 +122,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/unread_badge.h" #include "main/main_session.h" #include "main/main_session_settings.h" +#include "main/session/send_as_peers.h" #include "window/notifications_manager.h" #include "window/window_adaptive.h" #include "window/window_controller.h" @@ -791,6 +794,7 @@ HistoryWidget::HistoryWidget( }, lifetime()); setupScheduledToggle(); + setupSendAsToggle(); orderWidgets(); setupShortcuts(); } @@ -2111,6 +2115,7 @@ void HistoryWidget::showHistory( updateNotifyControls(); } refreshScheduledToggle(); + refreshSendAsToggle(); if (_showAtMsgId == ShowAtUnreadMsgId) { if (_history->scrollTopItem) { @@ -2368,6 +2373,49 @@ void HistoryWidget::refreshScheduledToggle() { } } +void HistoryWidget::setupSendAsToggle() { + session().sendAsPeers().updated( + ) | rpl::filter([=](not_null peer) { + return (peer == _peer); + }) | rpl::start_with_next([=] { + refreshSendAsToggle(); + updateControlsVisibility(); + updateControlsGeometry(); + }, lifetime()); +} + +void HistoryWidget::refreshSendAsToggle() { + Expects(_peer != nullptr); + + session().sendAsPeers().refresh(_peer); + const auto &list = session().sendAsPeers().list(_peer); + const auto has = _peer->canWrite() && (list.size() > 1); + if (!has) { + _sendAs.destroy(); + return; + } else if (_sendAs) { + return; + } + _sendAs.create(this, st::sendAsButton); + + using namespace rpl::mappers; + controller()->activeChatValue( + ) | rpl::map([=](const Dialogs::Key &key) -> PeerData* { + if (const auto history = key.history()) { + return history->peer; + } + return nullptr; + }) | rpl::filter_nullptr( + ) | rpl::map([=](not_null peer) { + return Data::PeerUserpicImageValue( + peer, + st::sendAsButton.size * style::DevicePixelRatio()); + }) | rpl::flatten_latest( + ) | rpl::start_with_next([=](QImage &&userpic) { + _sendAs->setUserpic(std::move(userpic)); + }, _sendAs->lifetime()); +} + bool HistoryWidget::contentOverlapped(const QRect &globalRect) { return (_attachDragAreas.document->overlaps(globalRect) || _attachDragAreas.photo->overlaps(globalRect) @@ -2469,6 +2517,9 @@ void HistoryWidget::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->hide(); } + if (_sendAs) { + _sendAs->hide(); + } _kbScroll->hide(); _fieldBarCancel->hide(); _attachToggle->hide(); @@ -2535,6 +2586,9 @@ void HistoryWidget::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->show(); } + if (_sendAs) { + _sendAs->show(); + } updateFieldPlaceholder(); if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { @@ -2567,9 +2621,11 @@ void HistoryWidget::updateControlsVisibility() { if (_ttlInfo) { _ttlInfo->hide(); } + if (_sendAs) { + _sendAs->hide(); + } _kbScroll->hide(); _fieldBarCancel->hide(); - _attachToggle->hide(); _tabbedSelectorToggle->hide(); _botKeyboardShow->hide(); _botKeyboardHide->hide(); @@ -4345,13 +4401,16 @@ void HistoryWidget::moveFieldControls() { _kbScroll->setGeometryToLeft(0, bottom, width(), keyboardHeight); } -// _attachToggle --------- _inlineResults -------------------------------------- _tabbedPanel --------- _fieldBarCancel +// _attachToggle (_sendAs) ------- _inlineResults ---------------------------------- _tabbedPanel -------- _fieldBarCancel // (_attachDocument|_attachPhoto) _field (_ttlInfo) (_scheduled) (_silent|_cmdStart|_kbShow) (_kbHide|_tabbedSelectorToggle) _send // (_botStart|_unblock|_joinChannel|_muteUnmute|_reportMessages) auto buttonsBottom = bottom - _attachToggle->height(); auto left = st::historySendRight; _attachToggle->moveToLeft(left, buttonsBottom); left += _attachToggle->width(); + if (_sendAs) { + _sendAs->moveToLeft(left, buttonsBottom); left += _sendAs->width(); + } _field->moveToLeft(left, bottom - _field->height() - st::historySendPadding); auto right = st::historySendRight; _send->moveToRight(right, buttonsBottom); right += _send->width(); diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 74a8a05f5..3406eda25 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -76,6 +76,7 @@ class GroupCallBar; class RequestsBar; struct PreparedList; class SendFilesWay; +class SendAsButton; enum class ReportReason; namespace Toast { class Instance; @@ -609,6 +610,8 @@ private: void setupScheduledToggle(); void refreshScheduledToggle(); + void setupSendAsToggle(); + void refreshSendAsToggle(); bool kbWasHidden() const; @@ -718,6 +721,7 @@ private: object_ptr _muteUnmute; object_ptr _reportMessages; object_ptr _attachToggle; + object_ptr _sendAs = { nullptr }; object_ptr _tabbedSelectorToggle; object_ptr _botKeyboardShow; object_ptr _botKeyboardHide; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index bdd5b40bb..4507d75d1 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "main/main_domain.h" #include "main/main_session_settings.h" +#include "main/session/send_as_peers.h" #include "mtproto/mtproto_config.h" #include "chat_helpers/stickers_emoji_pack.h" #include "chat_helpers/stickers_dice_pack.h" @@ -80,6 +81,7 @@ Session::Session( , _user(_data->processUser(user)) , _emojiStickersPack(std::make_unique(this)) , _diceStickersPacks(std::make_unique(this)) +, _sendAsPeers(std::make_unique(this)) , _supportHelper(Support::Helper::Create(this)) , _saveSettingsTimer([=] { saveSettings(); }) { Expects(_settings != nullptr); diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 01142396a..44693c498 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -58,6 +58,7 @@ namespace Main { class Account; class Domain; class SessionSettings; +class SendAsPeers; class Session final : public base::has_weak_ptr { public: @@ -113,6 +114,9 @@ public: [[nodiscard]] SessionSettings &settings() const { return *_settings; } + [[nodiscard]] SendAsPeers &sendAsPeers() const { + return *_sendAsPeers; + } void saveSettings(); void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay); @@ -180,6 +184,7 @@ private: // _emojiStickersPack depends on _data. const std::unique_ptr _emojiStickersPack; const std::unique_ptr _diceStickersPacks; + const std::unique_ptr _sendAsPeers; const std::unique_ptr _supportHelper; diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp new file mode 100644 index 000000000..a381f3cc1 --- /dev/null +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -0,0 +1,78 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "main/session/send_as_peers.h" + +#include "data/data_user.h" +#include "data/data_session.h" +#include "main/main_session.h" +#include "apiwrap.h" + +namespace Main { +namespace { + +constexpr auto kRequestEach = 30 * crl::time(1000); + +} // namespace + +SendAsPeers::SendAsPeers(not_null session) +: _session(session) +, _onlyMe({ session->user() }) { +} + +void SendAsPeers::refresh(not_null peer) { + if (!peer->isMegagroup()) { + return; + } + const auto now = crl::now(); + const auto i = _lastRequestTime.find(peer); + const auto when = (i == end(_lastRequestTime)) ? -1 : i->second; + if (when >= 0 && now < when + kRequestEach) { + return; + } + _lastRequestTime[peer] = now; + request(peer); +} + +const std::vector> &SendAsPeers::list(not_null peer) { + const auto i = _lists.find(peer); + return (i != end(_lists)) ? i->second : _onlyMe; +} + +rpl::producer> SendAsPeers::updated() const { + return _updates.events(); +} + +void SendAsPeers::request(not_null peer) { + _session->api().request(MTPchannels_GetSendAs( + peer->input + )).done([=](const MTPchannels_SendAsPeers &result) { + auto list = std::vector>(); + auto &owner = _session->data(); + result.match([&](const MTPDchannels_sendAsPeers &data) { + owner.processUsers(data.vusers()); + owner.processChats(data.vchats()); + for (const auto &id : data.vpeers().v) { + if (const auto peer = owner.peerLoaded(peerFromMTP(id))) { + list.push_back(peer); + } + } + }); + if (list.size() > 1) { + auto &now = _lists[peer]; + if (now != list) { + now = std::move(list); + _updates.fire_copy(peer); + } + } else if (const auto i = _lists.find(peer); i != end(_lists)) { + _lists.erase(i); + _updates.fire_copy(peer); + } + }).send(); +} + +} // namespace Main diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h new file mode 100644 index 000000000..951038a72 --- /dev/null +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -0,0 +1,40 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class PeerData; + +namespace Main { + +class Session; + +class SendAsPeers final { +public: + explicit SendAsPeers(not_null session); + + void refresh(not_null peer); + [[nodiscard]] const std::vector> &list( + not_null peer); + [[nodiscard]] rpl::producer> updated() const; + +private: + void request(not_null peer); + + const not_null _session; + const std::vector> _onlyMe; + + base::flat_map< + not_null, + std::vector>> _lists; + base::flat_map, crl::time> _lastRequestTime; + + rpl::event_stream> _updates; + +}; + +} // namespace Main diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index e856d8158..81b45668b 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -917,3 +917,28 @@ historyRequestsUserpics: GroupCallUserpics { align: align(left); } historyRequestsHeight: 33px; + +SendAsButton { + width: pixels; + height: pixels; + size: pixels; + activeBg: color; + activeFg: color; + cross: CrossAnimation; + duration: int; +} + +sendAsButton: SendAsButton { + width: 44px; + height: 46px; + size: 32px; + activeBg: activeButtonBg; + activeFg: activeButtonFg; + cross: CrossAnimation { + size: 32px; + skip: 10px; + stroke: 2px; + minScale: 0.3; + } + duration: 150; +} diff --git a/Telegram/SourceFiles/ui/controls/send_as_button.cpp b/Telegram/SourceFiles/ui/controls/send_as_button.cpp new file mode 100644 index 000000000..b9a124176 --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/send_as_button.cpp @@ -0,0 +1,70 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "ui/controls/send_as_button.h" + +#include "ui/effects/cross_animation.h" +#include "styles/style_chat.h" + +namespace Ui { + +SendAsButton::SendAsButton(QWidget *parent, const style::SendAsButton &st) +: AbstractButton(parent) +, _st(st) { + resize(_st.width, _st.height); +} + + +void SendAsButton::setUserpic(QImage userpic) { + _userpic = std::move(userpic); + update(); +} + +void SendAsButton::setActive(bool active) { + if (_active == active) { + return; + } + _active = active; + _activeAnimation.start( + [=] { update(); }, + _active ? 0. : 1., + _active ? 1. : 0., + _st.duration); +} + +void SendAsButton::paintEvent(QPaintEvent *e) { + auto p = Painter(this); + + const auto left = (width() - _st.size) / 2; + const auto top = (height() - _st.size) / 2; + + const auto active = _activeAnimation.value(_active ? 1. : 0.); + if (active < 1. && !_userpic.isNull()) { + p.drawImage(left, top, _userpic); + } + if (active > 0.) { + p.setOpacity(active); + + p.setPen(Qt::NoPen); + p.setBrush(_st.activeBg); + { + PainterHighQualityEnabler hq(p); + p.drawEllipse(left, top, _st.size, _st.size); + } + + CrossAnimation::paint( + p, + _st.cross, + _st.activeFg, + left, + top, + width(), + active); + } +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/send_as_button.h b/Telegram/SourceFiles/ui/controls/send_as_button.h new file mode 100644 index 000000000..d5eb47c3a --- /dev/null +++ b/Telegram/SourceFiles/ui/controls/send_as_button.h @@ -0,0 +1,39 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/widgets/buttons.h" +#include "ui/effects/animations.h" + +namespace style { +struct SendAsButton; +} // namespace style + +namespace Ui { + +class SendAsButton final : public AbstractButton { +public: + SendAsButton(QWidget *parent, const style::SendAsButton &st); + + void setUserpic(QImage userpic); + + void setActive(bool active); + +private: + void paintEvent(QPaintEvent *e) override; + + const style::SendAsButton &_st; + + Animations::Simple _activeAnimation; + bool _active = false; + + QImage _userpic; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/send_button.h b/Telegram/SourceFiles/ui/controls/send_button.h index da67bcbf5..0c174c8b3 100644 --- a/Telegram/SourceFiles/ui/controls/send_button.h +++ b/Telegram/SourceFiles/ui/controls/send_button.h @@ -13,7 +13,7 @@ namespace Ui { class SendButton final : public RippleButton { public: - SendButton(QWidget *parent); + explicit SendButton(QWidget *parent); static constexpr auto kSlowmodeDelayLimit = 100 * 60; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index a087769b5..20d23d0d7 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -179,6 +179,8 @@ PRIVATE ui/controls/invite_link_buttons.h ui/controls/invite_link_label.cpp ui/controls/invite_link_label.h + ui/controls/send_as_button.cpp + ui/controls/send_as_button.h ui/controls/send_button.cpp ui/controls/send_button.h ui/controls/who_read_context_action.cpp diff --git a/Telegram/lib_rpl b/Telegram/lib_rpl index df721be3f..94a42b775 160000 --- a/Telegram/lib_rpl +++ b/Telegram/lib_rpl @@ -1 +1 @@ -Subproject commit df721be3fa14a27dfc230d2e3c42bb1a7c9d0617 +Subproject commit 94a42b775ab4e46e5edeb88d8ed6c06f9e869c61