Start SendAsButton in HistoryWidget.

This commit is contained in:
John Preston 2021-11-09 16:10:51 +04:00
parent c849d17667
commit 4691cff3f6
15 changed files with 390 additions and 4 deletions

View File

@ -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

View File

@ -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<ChannelData*> channel) {
return (channel->flags() & ChannelDataFlag::CallNotEmpty);
}
rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size) {
return PeerUserpicImageValue(peer, size, ImageRoundRadius::Ellipse);
}
rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size,
ImageRoundRadius radius) {
return [=](auto consumer) {
auto result = rpl::lifetime();
struct State {
std::shared_ptr<CloudImageView> view;
rpl::lifetime waiting;
InMemoryKey key = {};
bool empty = true;
Fn<void()> push;
};
const auto state = result.make_state<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

View File

@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/combine.h>
#include "data/data_peer.h"
enum class ImageRoundRadius;
namespace Data {
template <typename ChangeType, typename Error, typename Generator>
@ -110,4 +112,12 @@ inline auto PeerFullFlagValue(
[[nodiscard]] bool IsUserOnline(not_null<UserData*> user);
[[nodiscard]] bool ChannelHasActiveCall(not_null<ChannelData*> channel);
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size);
[[nodiscard]] rpl::producer<QImage> PeerUserpicImageValue(
not_null<PeerData*> peer,
int size,
ImageRoundRadius radius);
} // namespace Data

View File

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

View File

@ -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<Ui::FlatButton> _muteUnmute;
object_ptr<Ui::FlatButton> _reportMessages;
object_ptr<Ui::IconButton> _attachToggle;
object_ptr<Ui::SendAsButton> _sendAs = { nullptr };
object_ptr<Ui::EmojiButton> _tabbedSelectorToggle;
object_ptr<Ui::IconButton> _botKeyboardShow;
object_ptr<Ui::IconButton> _botKeyboardHide;

View File

@ -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<Stickers::EmojiPack>(this))
, _diceStickersPacks(std::make_unique<Stickers::DicePacks>(this))
, _sendAsPeers(std::make_unique<SendAsPeers>(this))
, _supportHelper(Support::Helper::Create(this))
, _saveSettingsTimer([=] { saveSettings(); }) {
Expects(_settings != nullptr);

View File

@ -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<Stickers::EmojiPack> _emojiStickersPack;
const std::unique_ptr<Stickers::DicePacks> _diceStickersPacks;
const std::unique_ptr<SendAsPeers> _sendAsPeers;
const std::unique_ptr<Support::Helper> _supportHelper;

View File

@ -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(session)
, _onlyMe({ session->user() }) {
}
void SendAsPeers::refresh(not_null<PeerData*> 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<not_null<PeerData*>> &SendAsPeers::list(not_null<PeerData*> peer) {
const auto i = _lists.find(peer);
return (i != end(_lists)) ? i->second : _onlyMe;
}
rpl::producer<not_null<PeerData*>> SendAsPeers::updated() const {
return _updates.events();
}
void SendAsPeers::request(not_null<PeerData*> peer) {
_session->api().request(MTPchannels_GetSendAs(
peer->input
)).done([=](const MTPchannels_SendAsPeers &result) {
auto list = std::vector<not_null<PeerData*>>();
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

View File

@ -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*> session);
void refresh(not_null<PeerData*> peer);
[[nodiscard]] const std::vector<not_null<PeerData*>> &list(
not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<not_null<PeerData*>> updated() const;
private:
void request(not_null<PeerData*> peer);
const not_null<Session*> _session;
const std::vector<not_null<PeerData*>> _onlyMe;
base::flat_map<
not_null<PeerData*>,
std::vector<not_null<PeerData*>>> _lists;
base::flat_map<not_null<PeerData*>, crl::time> _lastRequestTime;
rpl::event_stream<not_null<PeerData*>> _updates;
};
} // namespace Main

View File

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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

@ -1 +1 @@
Subproject commit df721be3fa14a27dfc230d2e3c42bb1a7c9d0617
Subproject commit 94a42b775ab4e46e5edeb88d8ed6c06f9e869c61