From cabd7a276b064089043878e79b7c2d817c3483a3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 16 Dec 2020 15:58:53 +0400 Subject: [PATCH] Add 'invited' members to voice chats. --- .../icons/calls/group_calls_invited.png | Bin 0 -> 665 bytes .../icons/calls/group_calls_invited@2x.png | Bin 0 -> 1162 bytes .../icons/calls/group_calls_invited@3x.png | Bin 0 -> 1798 bytes Telegram/SourceFiles/calls/calls.style | 2 + .../SourceFiles/calls/calls_group_members.cpp | 118 +++++++++++++++--- Telegram/SourceFiles/data/data_session.cpp | 1 + Telegram/SourceFiles/data/data_session.h | 9 ++ 7 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 Telegram/Resources/icons/calls/group_calls_invited.png create mode 100644 Telegram/Resources/icons/calls/group_calls_invited@2x.png create mode 100644 Telegram/Resources/icons/calls/group_calls_invited@3x.png diff --git a/Telegram/Resources/icons/calls/group_calls_invited.png b/Telegram/Resources/icons/calls/group_calls_invited.png new file mode 100644 index 0000000000000000000000000000000000000000..cddca3ac91785b0a2003704ccdd8660c6a8496bd GIT binary patch literal 665 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i1|)m0dPoTz+}v^UoNd;NkZHmt_UIG}u<0{#!Ty@y8t^ekwvqkLCVt zZ(MneQk(BR?7?s$=*Rn?SMDZnNH92YHEZhOhZ|xagwJSo zQk3IilW1mm)qZ!jWK@TwQ_T4@EKCX#SsUc@lNVon5TmEgn%|g^t+1AngDpcdjfMC3 z!CQ;jHr{_foh2t|XUw)}cDL^hCJUl-V}(})XawxOt2Y0m(FP=Q?01=*SAFIWbBN&^t0yQoRY0?#HM0udD|!JH5^RaEEx7d zeX?g_%*qfAsotRV*C+QiZWWV`jJ25e`s*$M$zC^^?xQk~1DNZMI8I2vQp#q~wBzzi zk0PtNW|L*lcQh-UU-4PyMv2wat68aPb=7V*pG;=>%g&n=wf0-c9Lbhkv!|zbP0l+XkKqox#C literal 0 HcmV?d00001 diff --git a/Telegram/Resources/icons/calls/group_calls_invited@2x.png b/Telegram/Resources/icons/calls/group_calls_invited@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..921e358d077e5786e3dc4339e2ebd645f2465194 GIT binary patch literal 1162 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 N!uaD+&Aw5z{{SsP9M}K= literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 9a33dd382..56a2ee093 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -562,6 +562,8 @@ groupCallMemberColoredCrossLine: CrossLineAnimation(groupCallMemberInactiveCross fg: groupCallMemberMutedIcon; icon: icon {{ "calls/group_calls_unmuted", groupCallMemberActiveIcon }}; } +groupCallMemberInvited: icon {{ "calls/group_calls_invited", groupCallMemberInactiveIcon }}; +groupCallMemberInvitedPosition: point(2px, 12px); groupCallSettings: CallButton(callMicrophoneMute) { button: IconButton(callButton) { diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 415ae7889..3414f87dd 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/data_group_call.h" #include "data/data_peer_values.h" // Data::CanWriteValue. +#include "data/data_session.h" // Data::Session::invitedToCallUsers. #include "ui/paint/blobs.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" @@ -85,6 +86,7 @@ public: Active, Inactive, Muted, + Invited, }; void setSkipLevelUpdate(bool value); @@ -116,7 +118,9 @@ public: st::groupCallActiveButton.height); } bool actionDisabled() const override { - return peer()->isSelf() || !_delegate->rowCanMuteMembers(); + return peer()->isSelf() + || (_state == State::Invited) + || !_delegate->rowCanMuteMembers(); } QMargins actionMargins() const override { return QMargins( @@ -135,6 +139,15 @@ public: auto generatePaintUserpicCallback() -> PaintRoundImageCallback override; + void paintStatusText( + Painter &p, + const style::PeerListItem &st, + int x, + int y, + int availableWidth, + int outerWidth, + bool selected) override; + private: struct BlobsAnimation { BlobsAnimation( @@ -223,6 +236,8 @@ private: [[nodiscard]] std::unique_ptr createSelfRow(); [[nodiscard]] std::unique_ptr createRow( const Data::GroupCall::Participant &participant); + [[nodiscard]] std::unique_ptr createInvitedRow( + not_null user); void prepareRows(not_null real); //void repaintByTimer(); @@ -241,6 +256,7 @@ private: Row *findRow(not_null user) const; [[nodiscard]] Data::GroupCall *resolvedRealCall() const; + void appendInvitedUsers(); const base::weak_ptr _call; not_null _peer; @@ -248,6 +264,7 @@ private: // Use only resolvedRealCall() method, not this value directly. Data::GroupCall *_realCallRawValue = nullptr; uint64 _realId = 0; + bool _prepared = false; rpl::event_stream _toggleMuteRequests; rpl::event_stream> _kickMemberRequests; @@ -283,12 +300,7 @@ void Row::setSkipLevelUpdate(bool value) { void Row::updateState(const Data::GroupCall::Participant *participant) { setSsrc(participant ? participant->ssrc : 0); if (!participant) { - if (peer()->isSelf()) { - setCustomStatus(tr::lng_group_call_connecting(tr::now)); - } else { - setCustomStatus(QString()); - } - setState(State::Inactive); + setState(State::Invited); setSounding(false); setSpeaking(false); } else if (!participant->muted @@ -476,6 +488,34 @@ auto Row::generatePaintUserpicCallback() -> PaintRoundImageCallback { }; } +void Row::paintStatusText( + Painter &p, + const style::PeerListItem &st, + int x, + int y, + int availableWidth, + int outerWidth, + bool selected) { + if (_state != State::Invited) { + PeerListRow::paintStatusText( + p, + st, + x, + y, + availableWidth, + outerWidth, + selected); + return; + } + p.setFont(st::normalFont); + p.setPen(st::groupCallMemberNotJoinedStatus); + p.drawTextLeft( + x, + y, + outerWidth, + peer()->isSelf() ? "connecting..." : "invited"); +} + void Row::paintAction( Painter &p, int x, @@ -484,6 +524,20 @@ void Row::paintAction( bool selected, bool actionSelected) { auto size = actionSize(); + const auto iconRect = style::rtlrect( + x, + y, + size.width(), + size.height(), + outerWidth); + if (_state == State::Invited) { + _actionRipple = nullptr; + st::groupCallMemberInvited.paint( + p, + QPoint(x, y) + st::groupCallMemberInvitedPosition, + outerWidth); + return; + } if (_actionRipple) { _actionRipple->paint( p, @@ -494,12 +548,6 @@ void Row::paintAction( _actionRipple.reset(); } } - const auto iconRect = style::rtlrect( - x, - y, - size.width(), - size.height(), - outerWidth); const auto speaking = _speakingAnimation.value(_speaking ? 1. : 0.); const auto active = _activeAnimation.value( (_state == State::Active) ? 1. : 0.); @@ -649,6 +697,7 @@ void MembersController::subscribeToChanges(not_null real) { const auto user = update.was ? update.was->user : update.now->user; if (!update.now) { if (const auto row = findRow(user)) { + const auto owner = &user->owner(); if (user->isSelf()) { updateRow(row, nullptr); } else { @@ -660,6 +709,30 @@ void MembersController::subscribeToChanges(not_null real) { updateRow(update.was, *update.now); } }, _lifetime); + + if (_prepared) { + appendInvitedUsers(); + } +} + +void MembersController::appendInvitedUsers() { + for (const auto user : _peer->owner().invitedToCallUsers(_realId)) { + if (auto row = createInvitedRow(user)) { + delegate()->peerListAppendRow(std::move(row)); + } + } + delegate()->peerListRefreshRows(); + + using Invite = Data::Session::InviteToCall; + _peer->owner().invitesToCalls( + ) | rpl::filter([=](const Invite &invite) { + return (invite.id == _realId); + }) | rpl::start_with_next([=](const Invite &invite) { + if (auto row = createInvitedRow(invite.user)) { + delegate()->peerListAppendRow(std::move(row)); + delegate()->peerListRefreshRows(); + } + }, _lifetime); } void MembersController::updateRow( @@ -796,7 +869,12 @@ void MembersController::prepare() { delegate()->peerListAppendRow(std::move(row)); delegate()->peerListRefreshRows(); } + loadMoreRows(); + if (_realId) { + appendInvitedUsers(); + } + _prepared = true; } void MembersController::prepareRows(not_null real) { @@ -1056,7 +1134,9 @@ base::unique_qptr MembersController::rowContextMenu( tr::lng_context_send_message(tr::now), showHistory); const auto canKick = [&] { - if (const auto chat = _peer->asChat()) { + if (static_cast(row.get())->state() == Row::State::Invited) { + return false; + } else if (const auto chat = _peer->asChat()) { return chat->amCreator() || (chat->canBanMembers() && !chat->admins.contains(user)); } else if (const auto group = _peer->asMegagroup()) { @@ -1086,6 +1166,16 @@ std::unique_ptr MembersController::createRow( return result; } +std::unique_ptr MembersController::createInvitedRow( + not_null user) { + if (findRow(user)) { + return nullptr; + } + auto result = std::make_unique(this, user); + updateRow(result.get(), nullptr); + return result; +} + } // namespace GroupMembers::GroupMembers( diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index fa4df7c48..39b2353fd 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -841,6 +841,7 @@ void Session::registerInvitedToCallUser( } } _invitedToCallUsers[callId].emplace(user); + _invitesToCalls.fire({ callId, user }); } void Session::unregisterInvitedToCallUser( diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 811e8b8b7..ef3308820 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -166,6 +166,14 @@ public: not_null user); void unregisterInvitedToCallUser(uint64 callId, not_null user); + struct InviteToCall { + uint64 id = 0; + not_null user; + }; + [[nodiscard]] rpl::producer invitesToCalls() const { + return _invitesToCalls.events(); + } + void enumerateUsers(Fn)> action) const; void enumerateGroups(Fn)> action) const; void enumerateChannels(Fn)> action) const; @@ -923,6 +931,7 @@ private: base::flat_set> _heavyViewParts; base::flat_map> _groupCalls; + rpl::event_stream _invitesToCalls; base::flat_map>> _invitedToCallUsers; History *_topPromoted = nullptr;