From 1bd74fe47891b2c5c99bd64f8607211d3e2f7114 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Nov 2021 17:50:33 +0400 Subject: [PATCH] Choose a channel to send messages as. --- Telegram/SourceFiles/data/data_channel.cpp | 8 + .../SourceFiles/data/data_peer_values.cpp | 2 +- .../SourceFiles/history/history_widget.cpp | 20 +- .../main/session/send_as_peers.cpp | 55 ++++- .../SourceFiles/main/session/send_as_peers.h | 14 +- .../SourceFiles/ui/chat/choose_send_as.cpp | 210 ++++++++++++++++++ Telegram/SourceFiles/ui/chat/choose_send_as.h | 33 +++ 7 files changed, 319 insertions(+), 23 deletions(-) create mode 100644 Telegram/SourceFiles/ui/chat/choose_send_as.cpp create mode 100644 Telegram/SourceFiles/ui/chat/choose_send_as.h diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 5329183146..a6b6a95ca2 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_location.h" #include "data/data_histories.h" #include "data/data_group_call.h" +#include "main/main_session.h" +#include "main/session/send_as_peers.h" #include "base/unixtime.h" #include "history/history.h" #include "main/main_session.h" @@ -915,6 +917,12 @@ void ApplyChannelUpdate( MTP_inputNotifyPeer(channel->input), update.vnotify_settings()); + if (const auto sendAs = update.vdefault_send_as()) { + session->sendAsPeers().setChosen(channel, peerFromMTP(*sendAs)); + } else { + session->sendAsPeers().setChosen(channel, PeerId()); + } + // For clearUpTill() call. channel->owner().sendHistoryChangeNotifications(); } diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 668cad623c..a82493ac27 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -470,7 +470,7 @@ rpl::producer PeerUserpicImageValue( const auto state = result.make_state(); state->push = [=] { const auto key = peer->userpicUniqueKey(state->view); - const auto loading = !state->view || state->view->image(); + const auto loading = state->view && !state->view->image(); if (loading && !state->waiting) { peer->session().downloaderTaskFinished( diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index ba56c45730..9bcaa4d54b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/forward_options_box.h" #include "ui/chat/message_bar.h" #include "ui/chat/attach/attach_send_files_way.h" +#include "ui/chat/choose_send_as.h" #include "ui/image/image.h" #include "ui/special_buttons.h" #include "ui/controls/emoji_button.h" @@ -53,7 +54,6 @@ 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" @@ -2397,23 +2397,7 @@ void HistoryWidget::refreshSendAsToggle() { 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()); + Ui::SetupSendAsButton(_sendAs.data(), controller()); } bool HistoryWidget::contentOverlapped(const QRect &globalRect) { diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index a381f3cc13..41ebdd2f3e 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -38,7 +38,8 @@ void SendAsPeers::refresh(not_null peer) { request(peer); } -const std::vector> &SendAsPeers::list(not_null peer) { +const std::vector> &SendAsPeers::list( + not_null peer) const { const auto i = _lists.find(peer); return (i != end(_lists)) ? i->second : _onlyMe; } @@ -47,12 +48,60 @@ rpl::producer> SendAsPeers::updated() const { return _updates.events(); } +void SendAsPeers::saveChosen( + not_null peer, + not_null chosen) { + peer->session().api().request(MTPmessages_SaveDefaultSendAs( + peer->input, + chosen->input + )).send(); + + setChosen(peer, chosen->id); +} + +void SendAsPeers::setChosen(not_null peer, PeerId chosenId) { + if (chosen(peer) == chosenId) { + return; + } + const auto fallback = peer->amAnonymous() + ? peer + : peer->session().user(); + if (fallback->id == chosenId) { + _chosen.remove(peer); + } else { + _chosen[peer] = chosenId; + } + _updates.fire_copy(peer); +} + +PeerId SendAsPeers::chosen(not_null peer) const { + const auto i = _chosen.find(peer); + return (i != end(_chosen)) ? i->second : PeerId(); +} + +not_null SendAsPeers::resolveChosen( + not_null peer) const { + return ResolveChosen(peer, list(peer), chosen(peer)); +} + +not_null SendAsPeers::ResolveChosen( + not_null peer, + const std::vector> &list, + PeerId chosen) { + const auto i = ranges::find(list, chosen, &PeerData::id); + return (i != end(list)) + ? (*i) + : (peer->isMegagroup() && peer->amAnonymous()) + ? peer + : peer->session().user(); +} + void SendAsPeers::request(not_null peer) { - _session->api().request(MTPchannels_GetSendAs( + peer->session().api().request(MTPchannels_GetSendAs( peer->input )).done([=](const MTPchannels_SendAsPeers &result) { auto list = std::vector>(); - auto &owner = _session->data(); + auto &owner = peer->owner(); result.match([&](const MTPDchannels_sendAsPeers &data) { owner.processUsers(data.vusers()); owner.processChats(data.vchats()); diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h index 951038a72c..46d2ad7649 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.h +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -19,9 +19,20 @@ public: void refresh(not_null peer); [[nodiscard]] const std::vector> &list( - not_null peer); + not_null peer) const; [[nodiscard]] rpl::producer> updated() const; + void saveChosen(not_null peer, not_null chosen); + void setChosen(not_null peer, PeerId chosenId); + [[nodiscard]] PeerId chosen(not_null peer) const; + [[nodiscard]] not_null resolveChosen( + not_null peer) const; + + [[nodiscard]] static not_null ResolveChosen( + not_null peer, + const std::vector> &list, + PeerId chosen); + private: void request(not_null peer); @@ -32,6 +43,7 @@ private: not_null, std::vector>> _lists; base::flat_map, crl::time> _lastRequestTime; + base::flat_map, PeerId> _chosen; rpl::event_stream> _updates; diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp new file mode 100644 index 0000000000..1a0b9681dd --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -0,0 +1,210 @@ +/* +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/chat/choose_send_as.h" + +#include "boxes/peer_list_box.h" +#include "data/data_peer.h" +#include "data/data_channel.h" +#include "data/data_peer_values.h" +#include "history/history.h" +#include "ui/controls/send_as_button.h" +#include "window/window_session_controller.h" +#include "main/main_session.h" +#include "main/session/send_as_peers.h" +#include "lang/lang_keys.h" +#include "styles/style_calls.h" +#include "styles/style_boxes.h" +#include "styles/style_chat.h" + +namespace Ui { +namespace { + +class ListController final : public PeerListController { +public: + ListController( + std::vector> list, + not_null selected); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + + [[nodiscard]] not_null selected() const; + +private: + std::unique_ptr createRow(not_null peer); + + std::vector> _list; + not_null _selected; + +}; + +ListController::ListController( + std::vector> list, + not_null selected) +: PeerListController() +, _list(std::move(list)) +, _selected(selected) { +} + +Main::Session &ListController::session() const { + return _selected->session(); +} + +std::unique_ptr ListController::createRow( + not_null peer) { + auto result = std::make_unique(peer); + if (peer->isSelf()) { + result->setCustomStatus( + tr::lng_group_call_join_as_personal(tr::now)); + } else if (peer->isMegagroup()) { + result->setCustomStatus(u"Anonymous admin"_q); + } else if (const auto channel = peer->asChannel()) { + result->setCustomStatus(tr::lng_chat_status_subscribers( + tr::now, + lt_count, + channel->membersCount())); + } + return result; +} + +void ListController::prepare() { + delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled); + for (const auto &peer : _list) { + auto row = createRow(peer); + const auto raw = row.get(); + delegate()->peerListAppendRow(std::move(row)); + if (peer == _selected) { + delegate()->peerListSetRowChecked(raw, true); + raw->finishCheckedAnimation(); + } + } + delegate()->peerListRefreshRows(); +} + +void ListController::rowClicked(not_null row) { + const auto peer = row->peer(); + if (peer == _selected) { + return; + } + const auto previous = delegate()->peerListFindRow(_selected->id.value); + Assert(previous != nullptr); + delegate()->peerListSetRowChecked(previous, false); + delegate()->peerListSetRowChecked(row, true); + _selected = peer; +} + +not_null ListController::selected() const { + return _selected; +} + +} // namespace + +void ChooseSendAsBox( + not_null box, + std::vector> list, + not_null chosen, + Fn)> done) { + Expects(ranges::contains(list, chosen)); + Expects(done != nullptr); + + box->setWidth(st::groupCallJoinAsWidth); + box->setTitle(rpl::single(u"Send message as..."_q)); + const auto &labelSt = st::confirmPhoneAboutLabel; + box->addRow(object_ptr( + box, + tr::lng_group_call_join_as_about(), + labelSt)); + + auto &lifetime = box->lifetime(); + const auto delegate = lifetime.make_state< + PeerListContentDelegateSimple + >(); + const auto controller = lifetime.make_state( + list, + chosen); + controller->setStyleOverrides( + &st::peerListJoinAsList, + nullptr); + const auto content = box->addRow( + object_ptr(box, controller), + style::margins()); + delegate->setContent(content); + controller->setDelegate(delegate); + box->addButton(tr::lng_settings_save(), [=] { + done(controller->selected()); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); +} + +void SetupSendAsButton( + not_null button, + not_null window) { + using namespace rpl::mappers; + button->setClickedCallback([=] { + const auto history = window->activeChatCurrent().history(); + if (!history) { + return; + } + const auto peer = history->peer; + const auto session = &peer->session(); + const auto &list = session->sendAsPeers().list(peer); + if (list.size() < 2) { + return; + } + const auto done = [=](not_null sendAs) { + session->sendAsPeers().saveChosen(peer, sendAs); + }; + window->show(Box( + Ui::ChooseSendAsBox, + list, + session->sendAsPeers().resolveChosen(peer), + done)); + }); + + auto userpic = window->activeChatValue( + ) | rpl::map([=](const Dialogs::Key &key) -> PeerData* { + if (const auto history = key.history()) { + return history->peer->isMegagroup() + ? history->peer.get() + : nullptr; + } + return nullptr; + }) | rpl::filter_nullptr( + ) | rpl::map([=](not_null peer) { + const auto channel = peer->asMegagroup(); + + auto updates = rpl::single( + rpl::empty_value() + ) | rpl::then(channel->session().sendAsPeers().updated( + ) | rpl::filter( + _1 == channel + ) | rpl::to_empty); + + return rpl::combine( + std::move(updates), + channel->adminRightsValue() + ) | rpl::map([=] { + return channel->session().sendAsPeers().resolveChosen(channel); + }) | rpl::distinct_until_changed( + ) | rpl::map([=](not_null chosen) { + return Data::PeerUserpicImageValue( + chosen, + st::sendAsButton.size * style::DevicePixelRatio()); + }) | rpl::flatten_latest(); + }) | rpl::flatten_latest(); + + std::move( + userpic + ) | rpl::start_with_next([=](QImage &&userpic) { + button->setUserpic(std::move(userpic)); + }, button->lifetime()); + +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.h b/Telegram/SourceFiles/ui/chat/choose_send_as.h new file mode 100644 index 0000000000..eca82cc5b9 --- /dev/null +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.h @@ -0,0 +1,33 @@ +/* +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 "base/object_ptr.h" +#include "ui/layers/generic_box.h" + +class PeerData; + +namespace Window { +class SessionController; +} // namespace Window + +namespace Ui { + +class SendAsButton; + +void ChooseSendAsBox( + not_null box, + std::vector> list, + not_null chosen, + Fn)> done); + +void SetupSendAsButton( + not_null button, + not_null window); + +} // namespace Ui