Choose a channel to send messages as.
This commit is contained in:
parent
4691cff3f6
commit
1bd74fe478
|
@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_location.h"
|
#include "data/data_location.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
#include "data/data_group_call.h"
|
#include "data/data_group_call.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "main/session/send_as_peers.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -915,6 +917,12 @@ void ApplyChannelUpdate(
|
||||||
MTP_inputNotifyPeer(channel->input),
|
MTP_inputNotifyPeer(channel->input),
|
||||||
update.vnotify_settings());
|
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.
|
// For clearUpTill() call.
|
||||||
channel->owner().sendHistoryChangeNotifications();
|
channel->owner().sendHistoryChangeNotifications();
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,7 +470,7 @@ rpl::producer<QImage> PeerUserpicImageValue(
|
||||||
const auto state = result.make_state<State>();
|
const auto state = result.make_state<State>();
|
||||||
state->push = [=] {
|
state->push = [=] {
|
||||||
const auto key = peer->userpicUniqueKey(state->view);
|
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) {
|
if (loading && !state->waiting) {
|
||||||
peer->session().downloaderTaskFinished(
|
peer->session().downloaderTaskFinished(
|
||||||
|
|
|
@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/chat/forward_options_box.h"
|
#include "ui/chat/forward_options_box.h"
|
||||||
#include "ui/chat/message_bar.h"
|
#include "ui/chat/message_bar.h"
|
||||||
#include "ui/chat/attach/attach_send_files_way.h"
|
#include "ui/chat/attach/attach_send_files_way.h"
|
||||||
|
#include "ui/chat/choose_send_as.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
#include "ui/special_buttons.h"
|
#include "ui/special_buttons.h"
|
||||||
#include "ui/controls/emoji_button.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_web_page.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_photo.h"
|
#include "data/data_photo.h"
|
||||||
#include "data/data_peer_values.h"
|
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
|
@ -2397,23 +2397,7 @@ void HistoryWidget::refreshSendAsToggle() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_sendAs.create(this, st::sendAsButton);
|
_sendAs.create(this, st::sendAsButton);
|
||||||
|
Ui::SetupSendAsButton(_sendAs.data(), controller());
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
|
bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
|
||||||
|
|
|
@ -38,7 +38,8 @@ void SendAsPeers::refresh(not_null<PeerData*> peer) {
|
||||||
request(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);
|
const auto i = _lists.find(peer);
|
||||||
return (i != end(_lists)) ? i->second : _onlyMe;
|
return (i != end(_lists)) ? i->second : _onlyMe;
|
||||||
}
|
}
|
||||||
|
@ -47,12 +48,60 @@ rpl::producer<not_null<PeerData*>> SendAsPeers::updated() const {
|
||||||
return _updates.events();
|
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) {
|
void SendAsPeers::request(not_null<PeerData*> peer) {
|
||||||
_session->api().request(MTPchannels_GetSendAs(
|
peer->session().api().request(MTPchannels_GetSendAs(
|
||||||
peer->input
|
peer->input
|
||||||
)).done([=](const MTPchannels_SendAsPeers &result) {
|
)).done([=](const MTPchannels_SendAsPeers &result) {
|
||||||
auto list = std::vector<not_null<PeerData*>>();
|
auto list = std::vector<not_null<PeerData*>>();
|
||||||
auto &owner = _session->data();
|
auto &owner = peer->owner();
|
||||||
result.match([&](const MTPDchannels_sendAsPeers &data) {
|
result.match([&](const MTPDchannels_sendAsPeers &data) {
|
||||||
owner.processUsers(data.vusers());
|
owner.processUsers(data.vusers());
|
||||||
owner.processChats(data.vchats());
|
owner.processChats(data.vchats());
|
||||||
|
|
|
@ -19,9 +19,20 @@ public:
|
||||||
|
|
||||||
void refresh(not_null<PeerData*> peer);
|
void refresh(not_null<PeerData*> peer);
|
||||||
[[nodiscard]] const std::vector<not_null<PeerData*>> &list(
|
[[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;
|
[[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:
|
private:
|
||||||
void request(not_null<PeerData*> peer);
|
void request(not_null<PeerData*> peer);
|
||||||
|
|
||||||
|
@ -32,6 +43,7 @@ private:
|
||||||
not_null<PeerData*>,
|
not_null<PeerData*>,
|
||||||
std::vector<not_null<PeerData*>>> _lists;
|
std::vector<not_null<PeerData*>>> _lists;
|
||||||
base::flat_map<not_null<PeerData*>, crl::time> _lastRequestTime;
|
base::flat_map<not_null<PeerData*>, crl::time> _lastRequestTime;
|
||||||
|
base::flat_map<not_null<PeerData*>, PeerId> _chosen;
|
||||||
|
|
||||||
rpl::event_stream<not_null<PeerData*>> _updates;
|
rpl::event_stream<not_null<PeerData*>> _updates;
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue