Choose a channel to send messages as.

This commit is contained in:
John Preston 2021-11-09 17:50:33 +04:00
parent 4691cff3f6
commit 1bd74fe478
7 changed files with 319 additions and 23 deletions

View File

@ -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();
}

View File

@ -470,7 +470,7 @@ rpl::producer<QImage> PeerUserpicImageValue(
const auto state = result.make_state<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(

View File

@ -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<PeerData*> 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) {

View File

@ -38,7 +38,8 @@ void SendAsPeers::refresh(not_null<PeerData*> peer) {
request(peer);
}
const std::vector<not_null<PeerData*>> &SendAsPeers::list(not_null<PeerData*> peer) {
const std::vector<not_null<PeerData*>> &SendAsPeers::list(
not_null<PeerData*> peer) const {
const auto i = _lists.find(peer);
return (i != end(_lists)) ? i->second : _onlyMe;
}
@ -47,12 +48,60 @@ rpl::producer<not_null<PeerData*>> SendAsPeers::updated() const {
return _updates.events();
}
void SendAsPeers::saveChosen(
not_null<PeerData*> peer,
not_null<PeerData*> chosen) {
peer->session().api().request(MTPmessages_SaveDefaultSendAs(
peer->input,
chosen->input
)).send();
setChosen(peer, chosen->id);
}
void SendAsPeers::setChosen(not_null<PeerData*> 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<PeerData*> peer) const {
const auto i = _chosen.find(peer);
return (i != end(_chosen)) ? i->second : PeerId();
}
not_null<PeerData*> SendAsPeers::resolveChosen(
not_null<PeerData*> peer) const {
return ResolveChosen(peer, list(peer), chosen(peer));
}
not_null<PeerData*> SendAsPeers::ResolveChosen(
not_null<PeerData*> peer,
const std::vector<not_null<PeerData*>> &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<PeerData*> peer) {
_session->api().request(MTPchannels_GetSendAs(
peer->session().api().request(MTPchannels_GetSendAs(
peer->input
)).done([=](const MTPchannels_SendAsPeers &result) {
auto list = std::vector<not_null<PeerData*>>();
auto &owner = _session->data();
auto &owner = peer->owner();
result.match([&](const MTPDchannels_sendAsPeers &data) {
owner.processUsers(data.vusers());
owner.processChats(data.vchats());

View File

@ -19,9 +19,20 @@ public:
void refresh(not_null<PeerData*> peer);
[[nodiscard]] const std::vector<not_null<PeerData*>> &list(
not_null<PeerData*> peer);
not_null<PeerData*> peer) const;
[[nodiscard]] rpl::producer<not_null<PeerData*>> updated() const;
void saveChosen(not_null<PeerData*> peer, not_null<PeerData*> chosen);
void setChosen(not_null<PeerData*> peer, PeerId chosenId);
[[nodiscard]] PeerId chosen(not_null<PeerData*> peer) const;
[[nodiscard]] not_null<PeerData*> resolveChosen(
not_null<PeerData*> peer) const;
[[nodiscard]] static not_null<PeerData*> ResolveChosen(
not_null<PeerData*> peer,
const std::vector<not_null<PeerData*>> &list,
PeerId chosen);
private:
void request(not_null<PeerData*> peer);
@ -32,6 +43,7 @@ private:
not_null<PeerData*>,
std::vector<not_null<PeerData*>>> _lists;
base::flat_map<not_null<PeerData*>, crl::time> _lastRequestTime;
base::flat_map<not_null<PeerData*>, PeerId> _chosen;
rpl::event_stream<not_null<PeerData*>> _updates;

View File

@ -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<not_null<PeerData*>> list,
not_null<PeerData*> selected);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
[[nodiscard]] not_null<PeerData*> selected() const;
private:
std::unique_ptr<PeerListRow> createRow(not_null<PeerData*> peer);
std::vector<not_null<PeerData*>> _list;
not_null<PeerData*> _selected;
};
ListController::ListController(
std::vector<not_null<PeerData*>> list,
not_null<PeerData*> selected)
: PeerListController()
, _list(std::move(list))
, _selected(selected) {
}
Main::Session &ListController::session() const {
return _selected->session();
}
std::unique_ptr<PeerListRow> ListController::createRow(
not_null<PeerData*> peer) {
auto result = std::make_unique<PeerListRow>(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<PeerListRow*> 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<PeerData*> ListController::selected() const {
return _selected;
}
} // namespace
void ChooseSendAsBox(
not_null<GenericBox*> box,
std::vector<not_null<PeerData*>> list,
not_null<PeerData*> chosen,
Fn<void(not_null<PeerData*>)> 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<Ui::FlatLabel>(
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<ListController>(
list,
chosen);
controller->setStyleOverrides(
&st::peerListJoinAsList,
nullptr);
const auto content = box->addRow(
object_ptr<PeerListContent>(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<SendAsButton*> button,
not_null<Window::SessionController*> 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<PeerData*> 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<PeerData*> 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<PeerData*> 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

View File

@ -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<GenericBox*> box,
std::vector<not_null<PeerData*>> list,
not_null<PeerData*> chosen,
Fn<void(not_null<PeerData*>)> done);
void SetupSendAsButton(
not_null<SendAsButton*> button,
not_null<Window::SessionController*> window);
} // namespace Ui