diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 0fe6cf2af1..78b09d8d34 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -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) { diff --git a/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp b/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp index 137417a9e0..ca3ed26b13 100644 --- a/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp +++ b/Telegram/SourceFiles/calls/group/calls_choose_join_as.cpp @@ -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( diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 3c76a2d5b4..7f66d3d3b0 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -1200,7 +1200,12 @@ void GroupCall::playConnectingSoundOnce() { } not_null 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 { diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp index 895813038e..c71c7e195b 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp @@ -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( _parent, diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h index acfe97b293..8e06e24fd1 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h +++ b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h @@ -122,6 +122,9 @@ private: void updateLeftFade(); void updateRightFade(); + void receiveSomeMouseEvents(); + void receiveAllMouseEvents(); + const not_null _parent; const std::shared_ptr _show; const MessagesMode _mode; diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 96e21085b6..26dda01c39 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -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; diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_features.h b/Telegram/SourceFiles/chat_helpers/compose/compose_features.h index b6301b2a7d..67f816290a 100644 --- a/Telegram/SourceFiles/chat_helpers/compose/compose_features.h +++ b/Telegram/SourceFiles/chat_helpers/compose/compose_features.h @@ -30,6 +30,7 @@ struct ComposeFeatures { bool recordMediaMessage : 1 = true; bool editMessageStars : 1 = false; bool emojiOnlyPanel : 1 = false; + bool videoStream : 1 = false; }; } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 809956f464..6a3c18b95e 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -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 peer) { + _savedSendAs = peer; + api().request(MTPphone_SaveDefaultSendAs( + input(), + peer->input + )).send(); +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 69637c5978..cce8574bc2 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -213,6 +213,14 @@ public: return _messagesMinPrice.value(); } + [[nodiscard]] not_null resolveSendAs() const { + return _savedSendAs.current(); + } + [[nodiscard]] rpl::producer> sendAsValue() const { + return _savedSendAs.value(); + } + void saveSendAs(not_null peer); + private: enum class ApplySliceSource { FullReloaded, @@ -296,6 +304,8 @@ private: rpl::event_stream> _staleParticipantIds; rpl::lifetime _checkStaleLifetime; + rpl::variable> _savedSendAs; + bool _creator : 1 = false; bool _joinMuted : 1 = false; bool _recordVideo : 1 = false; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9aeabd9deb..3472266661 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -3286,8 +3286,9 @@ void HistoryWidget::refreshSuggestPostToggle() { void HistoryWidget::setupSendAsToggle() { session().sendAsPeers().updated( - ) | rpl::filter([=](not_null 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 { diff --git a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h index 95c18bc05f..85ac24eb99 100644 --- a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h +++ b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h @@ -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; + std::shared_ptr videoStream; MsgId topicRootId = 0; PeerId monoforumPeerId = 0; Fn showSlowmodeError; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 1e38afd364..9d28a3550b 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -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 peer) { +void ComposeControls::initSendAsButton( + not_null peer, + std::shared_ptr 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 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( - _wrap.get(), - st::sendAsButton); - Ui::SetupSendAsButton( - _sendAs.get(), - rpl::single(peer), - _regularWindow); + const auto &st = _st.chooseSendAs; + _sendAs = std::make_unique(_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; } diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h index d0f9787682..c6b59e9a2b 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -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 peer); + void initSendAsButton( + not_null peer, + std::shared_ptr 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 videoStream); void updateAttachBotsMenu(); void updateHeight(); void updateWrappingVisibility(); diff --git a/Telegram/SourceFiles/main/session/send_as_peers.cpp b/Telegram/SourceFiles/main/session/send_as_peers.cpp index e8bdaf0a26..568ae2e98b 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.cpp +++ b/Telegram/SourceFiles/main/session/send_as_peers.cpp @@ -44,44 +44,47 @@ SendAsPeers::SendAsPeers(not_null session) }, _lifetime); } -bool SendAsPeers::shouldChoose(not_null 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 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 &SendAsPeers::list( - not_null peer) const { - const auto i = _lists.find(peer); +const std::vector &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> &SendAsPeers::paidReactionList( - not_null peer) const { - const auto i = _paidReactionLists.find(peer); - return (i != end(_paidReactionLists)) ? i->second : _onlyMePaid; -} - -rpl::producer> SendAsPeers::updated() const { +rpl::producer SendAsPeers::updated() const { return _updates.events(); } @@ -108,7 +111,7 @@ void SendAsPeers::setChosen(not_null peer, PeerId chosenId) { } else { _chosen[peer] = chosenId; } - _updates.fire_copy(peer); + _updates.fire({ peer }); } PeerId SendAsPeers::chosen(not_null peer) const { @@ -118,7 +121,7 @@ PeerId SendAsPeers::chosen(not_null peer) const { not_null SendAsPeers::resolveChosen( not_null peer) const { - return ResolveChosen(peer, list(peer), chosen(peer)); + return ResolveChosen(peer, list({ peer }), chosen(peer)); } not_null SendAsPeers::ResolveChosen( @@ -141,14 +144,18 @@ not_null SendAsPeers::ResolveChosen( : fallback; } -void SendAsPeers::request(not_null 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(); - 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 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(); } diff --git a/Telegram/SourceFiles/main/session/send_as_peers.h b/Telegram/SourceFiles/main/session/send_as_peers.h index 9f831955e6..df09f6b461 100644 --- a/Telegram/SourceFiles/main/session/send_as_peers.h +++ b/Telegram/SourceFiles/main/session/send_as_peers.h @@ -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 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 peer; + SendAsType type = SendAsType::Message; +}; + class SendAsPeers final { public: explicit SendAsPeers(not_null session); - bool shouldChoose(not_null peer); - void refresh(not_null peer, bool force = false); - [[nodiscard]] const std::vector &list( - not_null peer) const; - [[nodiscard]] rpl::producer> updated() const; + bool shouldChoose(SendAsKey key); + void refresh(SendAsKey key, bool force = false); + [[nodiscard]] const std::vector &list(SendAsKey key) const; + [[nodiscard]] rpl::producer updated() const; void saveChosen(not_null peer, not_null chosen); void setChosen(not_null peer, PeerId chosenId); @@ -47,20 +67,17 @@ public: PeerId chosen); private: - void request(not_null peer, bool forPaidReactions = false); + void request(SendAsKey key); const not_null _session; const std::vector _onlyMe; const std::vector> _onlyMePaid; - base::flat_map, std::vector> _lists; - base::flat_map, crl::time> _lastRequestTime; + base::flat_map> _lists; + base::flat_map _lastRequestTime; base::flat_map, PeerId> _chosen; - base::flat_map< - not_null, - std::vector>> _paidReactionLists; - rpl::event_stream> _updates; + rpl::event_stream _updates; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index 735b754604..9f3dfcdf2c 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -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() { diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index ed5cc9e075..ace763772c 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -306,6 +306,7 @@ private: std::unique_ptr _captionFullView; std::unique_ptr _repostView; + std::shared_ptr _videoStream; base::weak_ptr _videoStreamCall; std::unique_ptr _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; diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index eca1c371bc..bb1c6333f2 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -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), diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.h b/Telegram/SourceFiles/media/stories/media_stories_reply.h index 1188d86f19..0ecc125882 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.h +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.h @@ -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 videoStream; friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default; friend inline bool operator==(ReplyAreaData, ReplyAreaData) = default; diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 090c05b9bd..c72a02309a 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -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); diff --git a/Telegram/SourceFiles/payments/payments_reaction_process.cpp b/Telegram/SourceFiles/payments/payments_reaction_process.cpp index 71d2395e3a..648b2259d5 100644 --- a/Telegram/SourceFiles/payments/payments_reaction_process.cpp +++ b/Telegram/SourceFiles/payments/payments_reaction_process.cpp @@ -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) { diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index 29a136e966..8020229edd 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -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; diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp index be0fa5601b..2da7689ca8 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.cpp @@ -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> ListController::clicked() const { void ChooseSendAsBox( not_null box, + const style::ChooseSendAs &st, std::vector list, not_null chosen, Fn)> 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( 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( list, chosen); - controller->setStyleOverrides( - &st::peerListJoinAsList, - nullptr); + controller->setStyleOverrides(&st.list, nullptr); controller->clicked( ) | rpl::start_with_next([=](not_null peer) { @@ -214,19 +212,21 @@ void ChooseSendAsBox( void SetupSendAsButton( not_null button, + const style::ChooseSendAs &st, rpl::producer active, - not_null window) { + std::shared_ptr show) { using namespace rpl::mappers; const auto current = button->lifetime().make_state< rpl::variable >(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 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 button, + const style::ChooseSendAs &st, + std::shared_ptr videoStream, + std::shared_ptr 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 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 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 button, + const style::ChooseSendAs &st, not_null 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 diff --git a/Telegram/SourceFiles/ui/chat/choose_send_as.h b/Telegram/SourceFiles/ui/chat/choose_send_as.h index f994b071d1..cce11a1c94 100644 --- a/Telegram/SourceFiles/ui/chat/choose_send_as.h +++ b/Telegram/SourceFiles/ui/chat/choose_send_as.h @@ -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 box, + const style::ChooseSendAs &st, std::vector list, not_null chosen, Fn)> done); void SetupSendAsButton( not_null button, + const style::ChooseSendAs &st, rpl::producer active, - not_null window); + std::shared_ptr show); void SetupSendAsButton( not_null button, + const style::ChooseSendAs &st, + std::shared_ptr videoStream, + std::shared_ptr show); + +void SetupSendAsButton( + not_null button, + const style::ChooseSendAs &st, not_null window); } // namespace Ui diff --git a/Telegram/SourceFiles/ui/controls/send_as_button.cpp b/Telegram/SourceFiles/ui/controls/send_as_button.cpp index 2ffbe77900..b6062f73c9 100644 --- a/Telegram/SourceFiles/ui/controls/send_as_button.cpp +++ b/Telegram/SourceFiles/ui/controls/send_as_button.cpp @@ -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 {