Support choosing send_as in video streams.

This commit is contained in:
John Preston
2025-10-28 19:51:52 +04:00
parent 1017d4cda3
commit 2d754c93a7
25 changed files with 372 additions and 193 deletions
-16
View File
@@ -743,22 +743,6 @@ groupCallJoinAsList: PeerList(groupCallInviteMembersList) {
statusPosition: point(73px, 28px);
}
}
peerListJoinAsList: PeerList(peerListBox) {
item: PeerListItem(peerListBoxItem) {
height: 56px;
checkbox: RoundImageCheckbox(defaultPeerListCheckbox) {
check: RoundCheckbox(defaultRoundCheckbox) {
size: 0px;
}
imageRadius: 19px;
imageSmallRadius: 15px;
}
photoSize: 38px;
photoPosition: point(24px, 9px);
namePosition: point(73px, 9px);
statusPosition: point(73px, 28px);
}
}
groupCallMultiSelect: MultiSelect(defaultMultiSelect) {
bg: groupCallMembersBg;
item: MultiSelectItem(defaultMultiSelectItem) {
@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "styles/style_layers.h"
#include "styles/style_calls.h"
#include "styles/style_chat_helpers.h"
namespace Calls::Group {
namespace {
@@ -224,7 +225,7 @@ void ChooseJoinAsBox(
&st::groupCallMultiSelect);
} else {
controller->setStyleOverrides(
&st::peerListJoinAsList,
&st::defaultChooseSendAs.list,
nullptr);
}
const auto content = box->addRow(
@@ -1200,7 +1200,12 @@ void GroupCall::playConnectingSoundOnce() {
}
not_null<PeerData*> GroupCall::messagesFrom() const {
return joinAs();
if (!videoStream()) {
return joinAs();
} else if (const auto real = lookupReal()) {
return real->resolveSendAs();
}
return _peer->session().user();
}
bool GroupCall::showChooseJoinAs() const {
@@ -902,40 +902,11 @@ void MessagesUi::setupMessagesWidget() {
updateBottomFade();
}, scroll->lifetime());
ReceiveSomeMouseEvents(scroll, [=](QPoint point) {
for (const auto &entry : _views) {
if (entry.failed || entry.top + entry.height <= point.y()) {
continue;
} else if (entry.top >= point.y()
|| entry.left >= point.x()
|| entry.left + entry.width <= point.x()) {
return false;
}
const auto padding = st::groupCallMessagePadding;
const auto userpicSize = st::groupCallUserpic;
const auto userpicPadding = st::groupCallUserpicPadding;
const auto leftSkip = userpicPadding.left()
+ userpicSize
+ userpicPadding.right();
const auto userpic = QRect(
entry.left + userpicPadding.left(),
entry.top + userpicPadding.top(),
userpicSize,
userpicSize);
const auto link = userpic.contains(point)
? entry.fromLink
: entry.text.getState(point - QPoint(
entry.left + leftSkip,
entry.top + padding.top()
), entry.width - leftSkip - padding.right()).link;
if (link) {
ActivateClickHandler(_messages, link, Qt::LeftButton);
}
return true;
}
return false;
});
if (_mode == MessagesMode::GroupCall) {
receiveSomeMouseEvents();
} else {
receiveAllMouseEvents();
}
_messages->paintRequest() | rpl::start_with_next([=](QRect clip) {
const auto start = scroll->scrollTop();
@@ -1116,6 +1087,47 @@ void MessagesUi::setupMessagesWidget() {
applyGeometry();
}
void MessagesUi::receiveSomeMouseEvents() {
ReceiveSomeMouseEvents(_scroll.get(), [=](QPoint point) {
for (const auto &entry : _views) {
if (entry.failed || entry.top + entry.height <= point.y()) {
continue;
} else if (entry.top >= point.y()
|| entry.left >= point.x()
|| entry.left + entry.width <= point.x()) {
return false;
}
const auto padding = st::groupCallMessagePadding;
const auto userpicSize = st::groupCallUserpic;
const auto userpicPadding = st::groupCallUserpicPadding;
const auto leftSkip = userpicPadding.left()
+ userpicSize
+ userpicPadding.right();
const auto userpic = QRect(
entry.left + userpicPadding.left(),
entry.top + userpicPadding.top(),
userpicSize,
userpicSize);
const auto link = userpic.contains(point)
? entry.fromLink
: entry.text.getState(point - QPoint(
entry.left + leftSkip,
entry.top + padding.top()
), entry.width - leftSkip - padding.right()).link;
if (link) {
ActivateClickHandler(_messages, link, Qt::LeftButton);
}
return true;
}
return false;
});
}
void MessagesUi::receiveAllMouseEvents() {
}
void MessagesUi::setupPinnedWidget() {
_pinnedScroll = std::make_unique<Ui::ElasticScroll>(
_parent,
@@ -122,6 +122,9 @@ private:
void updateLeftFade();
void updateRightFade();
void receiveSomeMouseEvents();
void receiveAllMouseEvents();
const not_null<QWidget*> _parent;
const std::shared_ptr<ChatHelpers::Show> _show;
const MessagesMode _mode;
@@ -201,6 +201,22 @@ ComposeFiles {
statusFg: color;
}
SendAsButton {
width: pixels;
height: pixels;
size: pixels;
activeBg: color;
activeFg: color;
cross: CrossAnimation;
duration: int;
}
ChooseSendAs {
button: SendAsButton;
label: FlatLabel;
list: PeerList;
}
ComposeControls {
bg: color;
radius: pixels;
@@ -232,6 +248,7 @@ ComposeControls {
boxField: InputField;
restrictionLabel: FlatLabel;
premiumRequired: ComposePremiumRequired;
chooseSendAs: ChooseSendAs;
}
ReportBox {
@@ -1436,6 +1453,42 @@ defaultRestrictionLabel: FlatLabel(defaultFlatLabel) {
textFg: placeholderFg;
align: align(top);
}
defaultSendAsButton: SendAsButton {
width: 44px;
height: 46px;
size: 28px;
activeBg: activeButtonBg;
activeFg: activeButtonFg;
cross: CrossAnimation {
size: 28px;
skip: 10px;
stroke: 1.5;
minScale: 0.3;
}
duration: 150;
}
defaultChooseSendAs: ChooseSendAs {
button: defaultSendAsButton;
label: FlatLabel(defaultFlatLabel) {
minWidth: 272px;
}
list: PeerList(peerListBox) {
item: PeerListItem(peerListBoxItem) {
height: 56px;
checkbox: RoundImageCheckbox(defaultPeerListCheckbox) {
check: RoundCheckbox(defaultRoundCheckbox) {
size: 0px;
}
imageRadius: 19px;
imageSmallRadius: 15px;
}
photoSize: 38px;
photoPosition: point(24px, 9px);
namePosition: point(73px, 9px);
statusPosition: point(73px, 28px);
}
}
}
defaultComposeControls: ComposeControls {
bg: historyComposeAreaBg;
radius: 0px;
@@ -1454,6 +1507,7 @@ defaultComposeControls: ComposeControls {
premium: defaultPremiumLimits;
boxField: defaultInputField;
restrictionLabel: defaultRestrictionLabel;
chooseSendAs: defaultChooseSendAs;
}
moreChatsBarHeight: 48px;
@@ -30,6 +30,7 @@ struct ComposeFeatures {
bool recordMediaMessage : 1 = true;
bool editMessageStars : 1 = false;
bool emojiOnlyPanel : 1 = false;
bool videoStream : 1 = false;
};
} // namespace ChatHelpers
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat.h"
#include "data/data_changes.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "main/main_session.h"
#include "calls/calls_instance.h"
#include "calls/group/calls_group_call.h"
@@ -70,6 +71,7 @@ GroupCall::GroupCall(
, _reloadByQueuedUpdatesTimer([=] { reload(); })
, _speakingByActiveFinishTimer([=] { checkFinishSpeakingByActive(); })
, _scheduleDate(scheduleDate)
, _savedSendAs(peer->session().user())
, _rtmp(rtmp)
, _conference(origin == GroupCallOrigin::Conference)
, _videoStream(origin == GroupCallOrigin::VideoStream)
@@ -548,6 +550,9 @@ void GroupCall::applyCallFields(const MTPDgroupCall &data) {
_allParticipantsLoaded
= (_serverParticipantsCount == _participants.size());
_conferenceInviteLink = qs(data.vinvite_link().value_or_empty());
if (const auto as = data.vdefault_send_as()) {
_savedSendAs = _peer->owner().peer(peerFromMTP(*as));
}
}
void GroupCall::applyLocalUpdate(
@@ -1129,4 +1134,12 @@ ApiWrap &GroupCall::api() const {
return session().api();
}
void GroupCall::saveSendAs(not_null<PeerData*> peer) {
_savedSendAs = peer;
api().request(MTPphone_SaveDefaultSendAs(
input(),
peer->input
)).send();
}
} // namespace Data
@@ -213,6 +213,14 @@ public:
return _messagesMinPrice.value();
}
[[nodiscard]] not_null<PeerData*> resolveSendAs() const {
return _savedSendAs.current();
}
[[nodiscard]] rpl::producer<not_null<PeerData*>> sendAsValue() const {
return _savedSendAs.value();
}
void saveSendAs(not_null<PeerData*> peer);
private:
enum class ApplySliceSource {
FullReloaded,
@@ -296,6 +304,8 @@ private:
rpl::event_stream<base::flat_set<UserId>> _staleParticipantIds;
rpl::lifetime _checkStaleLifetime;
rpl::variable<not_null<PeerData*>> _savedSendAs;
bool _creator : 1 = false;
bool _joinMuted : 1 = false;
bool _recordVideo : 1 = false;
+13 -10
View File
@@ -3286,8 +3286,9 @@ void HistoryWidget::refreshSuggestPostToggle() {
void HistoryWidget::setupSendAsToggle() {
session().sendAsPeers().updated(
) | rpl::filter([=](not_null<PeerData*> peer) {
return (peer == _peer);
) | rpl::filter([=](Main::SendAsKey key) {
return (key.peer == _peer)
&& (key.type == Main::SendAsType::Message);
}) | rpl::start_with_next([=] {
refreshSendAsToggle();
updateControlsVisibility();
@@ -3299,22 +3300,24 @@ void HistoryWidget::setupSendAsToggle() {
void HistoryWidget::refreshSendAsToggle() {
Expects(_peer != nullptr);
if (_editMsgId || !session().sendAsPeers().shouldChoose(_peer)) {
const auto key = Main::SendAsKey{ _peer, Main::SendAsType::Message };
if (_editMsgId || !session().sendAsPeers().shouldChoose(key)) {
_sendAs.destroy();
return;
} else if (_sendAs) {
return;
}
_sendAs.create(this, st::sendAsButton);
Ui::SetupSendAsButton(_sendAs.data(), controller());
const auto &st = st::defaultComposeControls.chooseSendAs;
_sendAs.create(this, st.button);
Ui::SetupSendAsButton(_sendAs.data(), st, controller());
}
bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
return (_attachDragAreas.document->overlaps(globalRect)
|| _attachDragAreas.photo->overlaps(globalRect)
|| (_autocomplete && _autocomplete->overlaps(globalRect))
|| (_tabbedPanel && _tabbedPanel->overlaps(globalRect))
|| (_inlineResults && _inlineResults->overlaps(globalRect)));
return _attachDragAreas.document->overlaps(globalRect)
|| _attachDragAreas.photo->overlaps(globalRect)
|| (_autocomplete && _autocomplete->overlaps(globalRect))
|| (_tabbedPanel && _tabbedPanel->overlaps(globalRect))
|| (_inlineResults && _inlineResults->overlaps(globalRect));
}
bool HistoryWidget::canWriteMessage() const {
@@ -14,6 +14,10 @@ enum class SendProgressType;
struct SendAction;
} // namespace Api
namespace Data {
class GroupCall;
} // namespace Data
class History;
namespace HistoryView::Controls {
@@ -66,6 +70,7 @@ struct WriteRestriction {
struct SetHistoryArgs {
required<History*> history;
std::shared_ptr<Data::GroupCall> videoStream;
MsgId topicRootId = 0;
PeerId monoforumPeerId = 0;
Fn<bool()> showSlowmodeError;
@@ -1056,7 +1056,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
return;
}
const auto peer = _history->peer;
initSendAsButton(peer);
initSendAsButton(peer, args.videoStream);
if (peer->isChat() && peer->asChat()->noParticipantInfo()) {
session().api().requestFullPeer(peer);
} else if (const auto channel = peer->asMegagroup()) {
@@ -1686,7 +1686,7 @@ void ComposeControls::init() {
) | rpl::start_with_next([=](const auto &id) {
unregisterDraftSources();
updateSendButtonType();
if (_history && updateSendAsButton()) {
if (_history && updateSendAsButton(nullptr)) {
updateControlsVisibility();
updateControlsGeometry(_wrap->size());
orderControls();
@@ -2529,24 +2529,33 @@ void ComposeControls::initSendButton() {
}, _send->lifetime());
}
void ComposeControls::initSendAsButton(not_null<PeerData*> peer) {
void ComposeControls::initSendAsButton(
not_null<PeerData*> peer,
std::shared_ptr<Data::GroupCall> videoStream) {
using namespace rpl::mappers;
using namespace Main;
// SendAsPeers::shouldChoose checks Data::CanSendAnything(PeerData*).
const auto key = SendAsKey{
peer,
videoStream ? SendAsType::VideoStream : SendAsType::Message,
};
rpl::combine(
rpl::single(peer) | rpl::then(
session().sendAsPeers().updated() | rpl::filter(_1 == peer)
rpl::single(key) | rpl::then(
session().sendAsPeers().updated() | rpl::filter(_1 == key)
),
Data::CanSendAnythingValue(peer, false)
(videoStream
? rpl::single(true)
: Data::CanSendAnythingValue(key.peer, false))
) | rpl::skip(1) | rpl::start_with_next([=] {
if (updateSendAsButton()) {
if (updateSendAsButton(videoStream)) {
updateControlsVisibility();
updateControlsGeometry(_wrap->size());
orderControls();
}
}, _historyLifetime);
updateSendAsButton();
updateSendAsButton(videoStream);
}
void ComposeControls::cancelInlineBot() {
@@ -3155,13 +3164,16 @@ void ComposeControls::updateMessagesTTLShown() {
}
}
bool ComposeControls::updateSendAsButton() {
bool ComposeControls::updateSendAsButton(
std::shared_ptr<Data::GroupCall> videoStream) {
const auto peer = _history ? _history->peer.get() : nullptr;
const auto type = videoStream
? Main::SendAsType::VideoStream
: Main::SendAsType::Message;
if (!_features.sendAs
|| !peer
|| !_regularWindow
|| isEditingMessage()
|| !session().sendAsPeers().shouldChoose(peer)) {
|| !session().sendAsPeers().shouldChoose({ peer, type })) {
if (!_sendAs) {
return false;
}
@@ -3170,13 +3182,13 @@ bool ComposeControls::updateSendAsButton() {
} else if (_sendAs) {
return false;
}
_sendAs = std::make_unique<Ui::SendAsButton>(
_wrap.get(),
st::sendAsButton);
Ui::SetupSendAsButton(
_sendAs.get(),
rpl::single(peer),
_regularWindow);
const auto &st = _st.chooseSendAs;
_sendAs = std::make_unique<Ui::SendAsButton>(_wrap.get(), st.button);
if (videoStream) {
Ui::SetupSendAsButton(_sendAs.get(), st, videoStream, _show);
} else {
Ui::SetupSendAsButton(_sendAs.get(), st, rpl::single(peer), _show);
}
return true;
}
@@ -46,6 +46,7 @@ struct MessagePosition;
struct Draft;
class DraftKey;
class PhotoMedia;
class GroupCall;
struct WebPageDraft;
struct MessageReactionsTopPaid;
} // namespace Data
@@ -77,6 +78,7 @@ class SuggestionsController;
namespace Main {
class Session;
struct SendAsKey;
} // namespace Main
namespace Webrtc {
@@ -296,7 +298,9 @@ private:
void initFieldAutocomplete();
void initTabbedSelector();
void initSendButton();
void initSendAsButton(not_null<PeerData*> peer);
void initSendAsButton(
not_null<PeerData*> peer,
std::shared_ptr<Data::GroupCall> videoStream);
void initWebpageProcess();
void initForwardProcess();
void initWriteRestriction();
@@ -308,7 +312,7 @@ private:
void updateSubmitSettings();
void updateSendButtonType();
void updateMessagesTTLShown();
bool updateSendAsButton();
bool updateSendAsButton(std::shared_ptr<Data::GroupCall> videoStream);
void updateAttachBotsMenu();
void updateHeight();
void updateWrappingVisibility();
@@ -44,44 +44,47 @@ SendAsPeers::SendAsPeers(not_null<Session*> session)
}, _lifetime);
}
bool SendAsPeers::shouldChoose(not_null<PeerData*> peer) {
refresh(peer);
const auto channel = peer->asBroadcast();
return Data::CanSendAnything(peer, false)
&& (list(peer).size() > 1)
bool SendAsPeers::shouldChoose(SendAsKey key) {
refresh(key);
if (list(key).size() < 2) {
return false;
}
if (key.type == SendAsType::VideoStream) {
return true;
}
const auto channel = key.peer->asBroadcast();
return Data::CanSendAnything(key.peer, false)
&& (!channel
|| channel->addsSignature()
|| channel->signatureProfiles());
}
void SendAsPeers::refresh(not_null<PeerData*> peer, bool force) {
if (!peer->isChannel()) {
void SendAsPeers::refresh(SendAsKey key, bool force) {
if (key.type != SendAsType::VideoStream && !key.peer->isChannel()) {
return;
}
const auto now = crl::now();
const auto i = _lastRequestTime.find(peer);
const auto i = _lastRequestTime.find(key);
const auto when = (i == end(_lastRequestTime)) ? -1 : i->second;
if (!force && (when >= 0 && now < when + kRequestEach)) {
return;
}
_lastRequestTime[peer] = now;
request(peer);
request(peer, true);
_lastRequestTime[key] = now;
request(key);
if (key.type == SendAsType::Message) {
request({ key.peer, SendAsType::PaidReaction });
}
}
const std::vector<SendAsPeer> &SendAsPeers::list(
not_null<PeerData*> peer) const {
const auto i = _lists.find(peer);
const std::vector<SendAsPeer> &SendAsPeers::list(SendAsKey key) const {
if (key.type != SendAsType::VideoStream && !key.peer->isChannel()) {
return _onlyMe;
}
const auto i = _lists.find(key);
return (i != end(_lists)) ? i->second : _onlyMe;
}
const std::vector<not_null<PeerData*>> &SendAsPeers::paidReactionList(
not_null<PeerData*> peer) const {
const auto i = _paidReactionLists.find(peer);
return (i != end(_paidReactionLists)) ? i->second : _onlyMePaid;
}
rpl::producer<not_null<PeerData*>> SendAsPeers::updated() const {
rpl::producer<SendAsKey> SendAsPeers::updated() const {
return _updates.events();
}
@@ -108,7 +111,7 @@ void SendAsPeers::setChosen(not_null<PeerData*> peer, PeerId chosenId) {
} else {
_chosen[peer] = chosenId;
}
_updates.fire_copy(peer);
_updates.fire({ peer });
}
PeerId SendAsPeers::chosen(not_null<PeerData*> peer) const {
@@ -118,7 +121,7 @@ PeerId SendAsPeers::chosen(not_null<PeerData*> peer) const {
not_null<PeerData*> SendAsPeers::resolveChosen(
not_null<PeerData*> peer) const {
return ResolveChosen(peer, list(peer), chosen(peer));
return ResolveChosen(peer, list({ peer }), chosen(peer));
}
not_null<PeerData*> SendAsPeers::ResolveChosen(
@@ -141,14 +144,18 @@ not_null<PeerData*> SendAsPeers::ResolveChosen(
: fallback;
}
void SendAsPeers::request(not_null<PeerData*> peer, bool forPaidReactions) {
void SendAsPeers::request(SendAsKey key) {
using Flag = MTPchannels_GetSendAs::Flag;
peer->session().api().request(MTPchannels_GetSendAs(
MTP_flags(forPaidReactions ? Flag::f_for_paid_reactions : Flag()),
peer->input
key.peer->session().api().request(MTPchannels_GetSendAs(
MTP_flags((key.type == SendAsType::PaidReaction)
? Flag::f_for_paid_reactions
: (key.type == SendAsType::VideoStream)
? Flag::f_for_live_stories
: Flag()),
key.peer->input
)).done([=](const MTPchannels_SendAsPeers &result) {
auto parsed = std::vector<SendAsPeer>();
auto &owner = peer->owner();
auto &owner = key.peer->owner();
result.match([&](const MTPDchannels_sendAsPeers &data) {
owner.processUsers(data.vusers());
owner.processChats(data.vchats());
@@ -165,26 +172,15 @@ void SendAsPeers::request(not_null<PeerData*> peer, bool forPaidReactions) {
}
}
});
if (forPaidReactions) {
auto peers = parsed | ranges::views::transform(
&SendAsPeer::peer
) | ranges::to_vector;
if (!peers.empty()) {
_paidReactionLists[peer] = std::move(peers);
} else {
_paidReactionLists.remove(peer);
}
} else {
if (parsed.size() > 1) {
auto &now = _lists[peer];
if (now != parsed) {
now = std::move(parsed);
_updates.fire_copy(peer);
}
} else if (const auto i = _lists.find(peer); i != end(_lists)) {
_lists.erase(i);
_updates.fire_copy(peer);
if (parsed.size() > 1) {
auto &now = _lists[key];
if (now != parsed) {
now = std::move(parsed);
_updates.fire_copy(key);
}
} else if (const auto i = _lists.find(key); i != end(_lists)) {
_lists.erase(i);
_updates.fire_copy(key);
}
}).send();
}
@@ -20,15 +20,35 @@ struct SendAsPeer {
friend inline auto operator<=>(SendAsPeer, SendAsPeer) = default;
};
enum class SendAsType : uchar {
Message,
PaidReaction,
VideoStream,
};
struct SendAsKey {
SendAsKey(
not_null<PeerData*> peer,
SendAsType type = SendAsType::Message)
: peer(peer)
, type(type) {
}
friend inline auto operator<=>(SendAsKey, SendAsKey) = default;
friend inline bool operator==(SendAsKey, SendAsKey) = default;
not_null<PeerData*> peer;
SendAsType type = SendAsType::Message;
};
class SendAsPeers final {
public:
explicit SendAsPeers(not_null<Session*> session);
bool shouldChoose(not_null<PeerData*> peer);
void refresh(not_null<PeerData*> peer, bool force = false);
[[nodiscard]] const std::vector<SendAsPeer> &list(
not_null<PeerData*> peer) const;
[[nodiscard]] rpl::producer<not_null<PeerData*>> updated() const;
bool shouldChoose(SendAsKey key);
void refresh(SendAsKey key, bool force = false);
[[nodiscard]] const std::vector<SendAsPeer> &list(SendAsKey key) const;
[[nodiscard]] rpl::producer<SendAsKey> updated() const;
void saveChosen(not_null<PeerData*> peer, not_null<PeerData*> chosen);
void setChosen(not_null<PeerData*> peer, PeerId chosenId);
@@ -47,20 +67,17 @@ public:
PeerId chosen);
private:
void request(not_null<PeerData*> peer, bool forPaidReactions = false);
void request(SendAsKey key);
const not_null<Session*> _session;
const std::vector<SendAsPeer> _onlyMe;
const std::vector<not_null<PeerData*>> _onlyMePaid;
base::flat_map<not_null<PeerData*>, std::vector<SendAsPeer>> _lists;
base::flat_map<not_null<PeerData*>, crl::time> _lastRequestTime;
base::flat_map<SendAsKey, std::vector<SendAsPeer>> _lists;
base::flat_map<SendAsKey, crl::time> _lastRequestTime;
base::flat_map<not_null<PeerData*>, PeerId> _chosen;
base::flat_map<
not_null<PeerData*>,
std::vector<not_null<PeerData*>>> _paidReactionLists;
rpl::event_stream<not_null<PeerData*>> _updates;
rpl::event_stream<SendAsKey> _updates;
rpl::lifetime _lifetime;
@@ -818,7 +818,7 @@ void Controller::rebuildFromContext(
_slider->show({
.index = _sliderCount ? _sliderIndex : _index,
.total = _sliderCount ? _sliderCount : shownCount(),
.videoStream = _videoStream,
.videoStream = videoStream(),
});
}
@@ -856,7 +856,7 @@ void Controller::show(
_context = context;
_waitingForId = {};
_waitingForDelta = 0;
_videoStream = (story->call() != nullptr);
_videoStream = story->call();
if (!_videoStream) {
clearVideoStreamCall();
}
@@ -921,7 +921,7 @@ void Controller::show(
.privacy = story->privacy(),
.edited = story->edited(),
.video = (document != nullptr),
.videoStream = _videoStream,
.videoStream = videoStream(),
.silent = (document && document->isSilentVideo()),
});
uiShow()->hideLayer(anim::type::instant);
@@ -943,7 +943,7 @@ void Controller::show(
.forwards = story->forwards(),
.views = story->views(),
.total = story->interactions(),
.type = RecentViewsTypeFor(peer, _videoStream),
.type = RecentViewsTypeFor(peer, videoStream()),
.canViewReactions = (!_videoStream
&& CanViewReactionsFor(peer)
&& !peer->isMegagroup()),
@@ -1052,7 +1052,7 @@ void Controller::subscribeToSession() {
.forwards = update.story->forwards(),
.views = update.story->views(),
.total = update.story->interactions(),
.type = RecentViewsTypeFor(peer, _videoStream),
.type = RecentViewsTypeFor(peer, videoStream()),
.canViewReactions = (!_videoStream
&& CanViewReactionsFor(peer)
&& !peer->isMegagroup()),
@@ -1932,7 +1932,7 @@ void Controller::clearVideoStreamCall() {
}
bool Controller::videoStream() const {
return _videoStream;
return _videoStream != nullptr;
}
rpl::lifetime &Controller::lifetime() {
@@ -306,6 +306,7 @@ private:
std::unique_ptr<CaptionFullView> _captionFullView;
std::unique_ptr<RepostView> _repostView;
std::shared_ptr<Data::GroupCall> _videoStream;
base::weak_ptr<Calls::GroupCall> _videoStreamCall;
std::unique_ptr<PaidReactionToast> _paidReactionToast;
rpl::lifetime _videoStreamLifetime;
@@ -318,7 +319,6 @@ private:
bool _layerShown = false;
bool _menuShown = false;
bool _tooltipShown = false;
bool _videoStream = false;
bool _paused = false;
FullStoryId _shown;
@@ -104,7 +104,7 @@ namespace {
[[nodiscard]] ChatHelpers::ComposeFeatures Features(bool videoStream) {
return {
.likes = !videoStream,
.sendAs = false,
.sendAs = videoStream,
.ttlInfo = false,
.attachments = !videoStream,
.botCommandSend = false,
@@ -858,7 +858,7 @@ void ReplyArea::show(
const auto streamChanged = (_data.videoStream != data.videoStream);
_data = data;
if (streamChanged) {
_controls->updateFeatures(Features(_data.videoStream));
_controls->updateFeatures(Features(_data.videoStream != nullptr));
_controls->setToggleCommentsButton(_data.videoStream
? _controller->commentsStateValue()
: nullptr);
@@ -917,6 +917,7 @@ void ReplyArea::show(
using namespace HistoryView;
_controls->setHistory({
.history = history,
.videoStream = _data.videoStream,
.showSlowmodeError = [=] { return showSlowmodeError(); },
.sendActionFactory = [=] { return prepareSendAction({}); },
.slowmodeSecondsLeft = SlowmodeSecondsLeft(history->peer),
@@ -24,6 +24,7 @@ class GroupCall;
} // namespace Calls
namespace Data {
class GroupCall;
struct ReactionId;
} // namespace Data
@@ -61,7 +62,7 @@ class Controller;
struct ReplyAreaData {
PeerData *peer = nullptr;
StoryId id = 0;
bool videoStream = false;
std::shared_ptr<Data::GroupCall> videoStream;
friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default;
friend inline bool operator==(ReplyAreaData, ReplyAreaData) = default;
@@ -689,6 +689,17 @@ storiesBoxInputField: InputField(defaultComposeFilesField) {
borderFgActive: storiesComposeBlue;
menu: storiesPopupMenu;
}
storiesChooseSendAs: ChooseSendAs(defaultChooseSendAs) {
button: SendAsButton(defaultSendAsButton) {
width: 42px;
height: 42px;
}
label: FlatLabel(defaultFlatLabel) {
minWidth: 272px;
textFg: storiesComposeWhiteText;
}
list: groupCallJoinAsList;
}
storiesComposeControls: ComposeControls(defaultComposeControls) {
bg: storiesComposeBg;
radius: 21px;
@@ -866,6 +877,7 @@ storiesComposeControls: ComposeControls(defaultComposeControls) {
position: point(37px, 14px);
icon: icon{{ "emoji/premium_lock", storiesComposeGrayIcon, point(13px, 14px) }};
}
chooseSendAs: storiesChooseSendAs;
}
storiesViewsMenu: PopupMenu(storiesPopupMenuWithIcons) {
scrollPadding: margins(0px, 6px, 0px, 4px);
@@ -245,7 +245,11 @@ void ShowPaidReactionDetails(
};
const auto linked = item->discussionPostOriginalSender();
const auto channel = (linked ? linked : item->history()->peer.get());
const auto channels = session->sendAsPeers().paidReactionList(channel);
const auto channels = session->sendAsPeers().list(
{ channel, Main::SendAsType::PaidReaction }
) | ranges::views::transform(
&Main::SendAsPeer::peer
) | ranges::to_vector;
const auto topPaid = item->topPaidReactionsWithLocal();
top.reserve(topPaid.size() + 2 + channels.size());
for (const auto &entry : topPaid) {
-25
View File
@@ -848,31 +848,6 @@ reactionsTabs: MultiSelect(defaultMultiSelect) {
}
reactionsTabIconSkip: 3px;
SendAsButton {
width: pixels;
height: pixels;
size: pixels;
activeBg: color;
activeFg: color;
cross: CrossAnimation;
duration: int;
}
sendAsButton: SendAsButton {
width: 44px;
height: 46px;
size: 28px;
activeBg: activeButtonBg;
activeFg: activeButtonFg;
cross: CrossAnimation {
size: 28px;
skip: 10px;
stroke: 1.5;
minScale: 0.3;
}
duration: 150;
}
reactionInlinePadding: margins(5px, 2px, 7px, 2px);
reactionInlineSize: 18px;
reactionInlineImage: 32px;
+59 -14
View File
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/chat/choose_send_as.h"
#include "boxes/peer_list_box.h"
#include "data/data_group_call.h"
#include "data/data_peer.h"
#include "data/data_channel.h"
#include "data/data_peer_values.h"
@@ -21,7 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "settings/settings_premium.h"
#include "styles/style_calls.h"
#include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
@@ -171,6 +171,7 @@ rpl::producer<not_null<PeerData*>> ListController::clicked() const {
void ChooseSendAsBox(
not_null<GenericBox*> box,
const style::ChooseSendAs &st,
std::vector<Main::SendAsPeer> list,
not_null<PeerData*> chosen,
Fn<bool(not_null<PeerData*>)> done) {
@@ -179,11 +180,10 @@ void ChooseSendAsBox(
box->setWidth(st::groupCallJoinAsWidth);
box->setTitle(tr::lng_send_as_title());
const auto &labelSt = st::confirmPhoneAboutLabel;
box->addRow(object_ptr<Ui::FlatLabel>(
box,
tr::lng_group_call_join_as_about(),
labelSt));
st.label));
auto &lifetime = box->lifetime();
const auto delegate = lifetime.make_state<
@@ -192,9 +192,7 @@ void ChooseSendAsBox(
const auto controller = lifetime.make_state<ListController>(
list,
chosen);
controller->setStyleOverrides(
&st::peerListJoinAsList,
nullptr);
controller->setStyleOverrides(&st.list, nullptr);
controller->clicked(
) | rpl::start_with_next([=](not_null<PeerData*> peer) {
@@ -214,19 +212,21 @@ void ChooseSendAsBox(
void SetupSendAsButton(
not_null<SendAsButton*> button,
const style::ChooseSendAs &st,
rpl::producer<PeerData*> active,
not_null<Window::SessionController*> window) {
std::shared_ptr<ChatHelpers::Show> show) {
using namespace rpl::mappers;
const auto current = button->lifetime().make_state<
rpl::variable<PeerData*>
>(std::move(active));
button->setClickedCallback([=] {
button->setClickedCallback([=, &st] {
const auto peer = current->current();
if (!peer) {
return;
}
const auto key = Main::SendAsKey{ peer, Main::SendAsType::Message };
const auto session = &peer->session();
const auto &list = session->sendAsPeers().list(peer);
const auto &list = session->sendAsPeers().list(key);
if (list.size() < 2) {
return;
}
@@ -239,7 +239,7 @@ void SetupSendAsButton(
&& i->premiumRequired
&& !sendAs->session().premium()) {
Settings::ShowPremiumPromoToast(
window->uiShow(),
show,
tr::lng_send_as_premium_required(
tr::now,
lt_link,
@@ -254,13 +254,15 @@ void SetupSendAsButton(
session->sendAsPeers().saveChosen(peer, sendAs);
return true;
};
window->show(Box(
show->show(Box(
Ui::ChooseSendAsBox,
st,
list,
session->sendAsPeers().resolveChosen(peer),
done));
});
const auto size = st.button.size;
auto userpic = current->value(
) | rpl::filter([=](PeerData *peer) {
return peer && peer->isChannel();
@@ -271,7 +273,7 @@ void SetupSendAsButton(
rpl::empty
) | rpl::then(channel->session().sendAsPeers().updated(
) | rpl::filter(
_1 == channel
_1 == Main::SendAsKey(channel, Main::SendAsType::Message)
) | rpl::to_empty);
return rpl::combine(
@@ -283,7 +285,7 @@ void SetupSendAsButton(
) | rpl::map([=](not_null<PeerData*> chosen) {
return Data::PeerUserpicImageValue(
chosen,
st::sendAsButton.size * style::DevicePixelRatio());
size * style::DevicePixelRatio());
}) | rpl::flatten_latest();
}) | rpl::flatten_latest();
@@ -296,12 +298,55 @@ void SetupSendAsButton(
void SetupSendAsButton(
not_null<SendAsButton*> button,
const style::ChooseSendAs &st,
std::shared_ptr<Data::GroupCall> videoStream,
std::shared_ptr<ChatHelpers::Show> show) {
const auto peer = videoStream->peer();
const auto type = Main::SendAsType::VideoStream;
const auto key = Main::SendAsKey{ peer, type };
button->setClickedCallback([=, &st] {
const auto session = &peer->session();
const auto &list = session->sendAsPeers().list(key);
if (list.size() < 2) {
return;
}
const auto done = [=](not_null<PeerData*> sendAs) {
videoStream->saveSendAs(sendAs);
return true;
};
show->show(Box(
Ui::ChooseSendAsBox,
st,
list,
videoStream->resolveSendAs(),
done));
});
const auto size = st.button.size;
auto userpic = videoStream->sendAsValue(
) | rpl::distinct_until_changed(
) | rpl::map([=](not_null<PeerData*> chosen) {
return Data::PeerUserpicImageValue(
chosen,
size * style::DevicePixelRatio());
}) | rpl::flatten_latest();
std::move(
userpic
) | rpl::start_with_next([=](QImage &&userpic) {
button->setUserpic(std::move(userpic));
}, button->lifetime());
}
void SetupSendAsButton(
not_null<SendAsButton*> button,
const style::ChooseSendAs &st,
not_null<Window::SessionController*> window) {
auto active = window->activeChatValue(
) | rpl::map([=](Dialogs::Key key) {
return key.history() ? key.history()->peer.get() : nullptr;
});
SetupSendAsButton(button, std::move(active), window);
SetupSendAsButton(button, st, std::move(active), window->uiShow());
}
} // namespace Ui
+22 -1
View File
@@ -12,6 +12,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class PeerData;
namespace style {
struct ChooseSendAs;
} // namespace style
namespace Data {
class GroupCall;
} // namespace Data
namespace ChatHelpers {
class Show;
} // namespace ChatHelpers
namespace Main {
struct SendAsPeer;
} // namespace Main
@@ -26,17 +38,26 @@ class SendAsButton;
void ChooseSendAsBox(
not_null<GenericBox*> box,
const style::ChooseSendAs &st,
std::vector<Main::SendAsPeer> list,
not_null<PeerData*> chosen,
Fn<bool(not_null<PeerData*>)> done);
void SetupSendAsButton(
not_null<SendAsButton*> button,
const style::ChooseSendAs &st,
rpl::producer<PeerData*> active,
not_null<Window::SessionController*> window);
std::shared_ptr<ChatHelpers::Show> show);
void SetupSendAsButton(
not_null<SendAsButton*> button,
const style::ChooseSendAs &st,
std::shared_ptr<Data::GroupCall> videoStream,
std::shared_ptr<ChatHelpers::Show> show);
void SetupSendAsButton(
not_null<SendAsButton*> button,
const style::ChooseSendAs &st,
not_null<Window::SessionController*> window);
} // namespace Ui
@@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/cross_animation.h"
#include "ui/painter.h"
#include "styles/style_chat.h"
#include "styles/style_chat_helpers.h"
namespace Ui {