From be611c19204af79f2ca5ba2b033afc094e07a564 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 30 Mar 2025 23:22:33 +0500 Subject: [PATCH] Show confcall users that are only on blockchain. --- Telegram/Resources/langs/lang.strings | 1 + .../calls/group/calls_group_members.cpp | 170 +++++++++++++++--- .../calls/group/calls_group_members_row.cpp | 81 ++++++--- .../calls/group/calls_group_members_row.h | 7 +- 4 files changed, 209 insertions(+), 50 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 86ea166e86..85bbaec270 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -4759,6 +4759,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_group_call_invite" = "Invite Members"; "lng_group_call_invite_conf" = "Add People"; "lng_group_call_invited_status" = "invited"; +"lng_group_call_blockchain_only_status" = "syncing..."; "lng_group_call_muted_by_me_status" = "muted for you"; "lng_group_call_invite_title" = "Invite members"; "lng_group_call_invite_button" = "Invite"; diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index cf5c3b42bf..4d14d0f812 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -108,6 +108,8 @@ private: const Data::GroupCallParticipant &participant); [[nodiscard]] std::unique_ptr createInvitedRow( not_null participantPeer); + [[nodiscard]] std::unique_ptr createWithAccessRow( + not_null participantPeer); [[nodiscard]] bool isMe(not_null participantPeer) const; void prepareRows(not_null real); @@ -128,7 +130,8 @@ private: void updateRow( not_null row, const std::optional &was, - const Data::GroupCallParticipant *participant); + const Data::GroupCallParticipant *participant, + Row::State noParticipantState = Row::State::Invited); void updateRowInSoundingMap( not_null row, bool wasSounding, @@ -162,8 +165,13 @@ private: const VideoEndpoint &endpoint, bool active); - void appendInvitedUsers(); + void partitionRows(); + void setupInvitedUsers(); + [[nodiscard]] bool appendInvitedUsers(); + void setupWithAccessUsers(); + [[nodiscard]] bool appendWithAccessUsers(); void scheduleRaisedHandStatusRemove(); + void refreshWithAccessRows(base::flat_set &&nowIds); void hideRowsWithVideoExcept(const VideoEndpoint &large); void showAllHiddenRows(); @@ -205,6 +213,8 @@ private: Ui::RoundRect _narrowRoundRect; QImage _narrowShadow; + base::flat_set _withAccess; + rpl::lifetime _lifetime; }; @@ -414,6 +424,9 @@ void Members::Controller::subscribeToChanges(not_null real) { if (const auto row = findRow(participantPeer)) { if (isMe(participantPeer)) { updateRow(row, update.was, nullptr); + } else if (_withAccess.contains(peerToUser(participantPeer->id))) { + updateRow(row, update.was, nullptr, Row::State::WithAccess); + partitionRows(); } else { removeRow(row); delegate()->peerListRefreshRows(); @@ -431,10 +444,6 @@ void Members::Controller::subscribeToChanges(not_null real) { ) | rpl::start_with_next([=](const VideoStateToggle &update) { toggleVideoEndpointActive(update.endpoint, update.value); }, _lifetime); - - if (_prepared) { - appendInvitedUsers(); - } } void Members::Controller::toggleVideoEndpointActive( @@ -481,13 +490,21 @@ void Members::Controller::toggleVideoEndpointActive( } -void Members::Controller::appendInvitedUsers() { +bool Members::Controller::appendInvitedUsers() { + auto changed = false; if (const auto id = _call->id()) { for (const auto &user : _peer->owner().invitedToCallUsers(id)) { if (auto row = createInvitedRow(user)) { delegate()->peerListAppendRow(std::move(row)); + changed = true; } } + } + return changed; +} + +void Members::Controller::setupInvitedUsers() { + if (appendInvitedUsers()) { delegate()->peerListRefreshRows(); } @@ -503,15 +520,79 @@ void Members::Controller::appendInvitedUsers() { }, _lifetime); } +bool Members::Controller::appendWithAccessUsers() { + auto changed = false; + for (const auto id : _withAccess) { + if (auto row = createWithAccessRow(_peer->owner().user(id))) { + changed = true; + delegate()->peerListAppendRow(std::move(row)); + } + } + return changed; +} + +void Members::Controller::setupWithAccessUsers() { + const auto conference = _call->conferenceCall().get(); + if (!conference) { + return; + } + conference->participantsWithAccessValue( + ) | rpl::start_with_next([=](base::flat_set &&nowIds) { + for (auto i = begin(_withAccess); i != end(_withAccess);) { + const auto oldId = *i; + if (nowIds.remove(oldId)) { + ++i; + continue; + } + const auto user = _peer->owner().user(oldId); + if (const auto row = findRow(user)) { + if (row->state() == Row::State::WithAccess) { + removeRow(row); + } + } + i = _withAccess.erase(i); + } + auto partition = false; + auto partitionChecked = false; + for (const auto nowId : nowIds) { + const auto user = _peer->owner().user(nowId); + if (!findRow(user)) { + if (auto row = createWithAccessRow(user)) { + if (!partitionChecked) { + partitionChecked = true; + if (const auto count = delegate()->peerListFullRowsCount()) { + const auto last = delegate()->peerListRowAt(count - 1); + const auto state = static_cast(last.get())->state(); + if (state == Row::State::Invited) { + partition = true; + } + } + } + delegate()->peerListAppendRow(std::move(row)); + } + } + _withAccess.emplace(nowId); + } + if (partition) { + delegate()->peerListPartitionRows([](const PeerListRow &row) { + const auto state = static_cast(row).state(); + return (state != Row::State::Invited); + }); + } + delegate()->peerListRefreshRows(); + }, _lifetime); +} + void Members::Controller::updateRow( const std::optional &was, const Data::GroupCallParticipant &now) { - auto reorderIfInvitedBefore = 0; + auto reorderIfNonRealBefore = 0; auto checkPosition = (Row*)nullptr; auto addedToBottom = (Row*)nullptr; if (const auto row = findRow(now.peer)) { - if (row->state() == Row::State::Invited) { - reorderIfInvitedBefore = row->absoluteIndex(); + if (row->state() == Row::State::Invited + || row->state() == Row::State::WithAccess) { + reorderIfNonRealBefore = row->absoluteIndex(); } updateRow(row, was, &now); if ((now.speaking && (!was || !was->speaking)) @@ -523,7 +604,7 @@ void Members::Controller::updateRow( if (row->speaking()) { delegate()->peerListPrependRow(std::move(row)); } else { - reorderIfInvitedBefore = delegate()->peerListFullRowsCount(); + reorderIfNonRealBefore = delegate()->peerListFullRowsCount(); if (now.raisedHandRating != 0) { checkPosition = row.get(); } else { @@ -533,20 +614,19 @@ void Members::Controller::updateRow( } delegate()->peerListRefreshRows(); } - static constexpr auto kInvited = Row::State::Invited; const auto reorder = [&] { - const auto count = reorderIfInvitedBefore; + const auto count = reorderIfNonRealBefore; if (count <= 0) { return false; } const auto row = delegate()->peerListRowAt( - reorderIfInvitedBefore - 1).get(); - return (static_cast(row)->state() == kInvited); + reorderIfNonRealBefore - 1).get(); + using State = Row::State; + const auto state = static_cast(row)->state(); + return (state == State::Invited) || (state == State::WithAccess); }(); if (reorder) { - delegate()->peerListPartitionRows([](const PeerListRow &row) { - return static_cast(row).state() != kInvited; - }); + partitionRows(); } if (checkPosition) { checkRowPosition(checkPosition); @@ -570,6 +650,24 @@ void Members::Controller::updateRow( } } +void Members::Controller::partitionRows() { + auto hadWithAccess = false; + delegate()->peerListPartitionRows([&](const PeerListRow &row) { + using State = Row::State; + const auto state = static_cast(row).state(); + if (state == State::WithAccess) { + hadWithAccess = true; + } + return (state != State::Invited) && (state != State::WithAccess); + }); + if (hadWithAccess) { + delegate()->peerListPartitionRows([](const PeerListRow &row) { + const auto state = static_cast(row).state(); + return (state != Row::State::Invited); + }); + } +} + bool Members::Controller::allRowsAboveAreSpeaking(not_null row) const { const auto count = delegate()->peerListFullRowsCount(); for (auto i = 0; i != count; ++i) { @@ -692,14 +790,21 @@ void Members::Controller::checkRowPosition(not_null row) { void Members::Controller::updateRow( not_null row, const std::optional &was, - const Data::GroupCallParticipant *participant) { + const Data::GroupCallParticipant *participant, + Row::State noParticipantState) { const auto wasSounding = row->sounding(); const auto wasSsrc = was ? was->ssrc : 0; const auto wasAdditionalSsrc = was ? GetAdditionalAudioSsrc(was->videoParams) : 0; row->setSkipLevelUpdate(_skipRowLevelUpdate); - row->updateState(participant); + if (participant) { + row->updateState(*participant); + } else if (noParticipantState == Row::State::WithAccess) { + row->updateStateWithAccess(); + } else { + row->updateStateInvited(); + } const auto wasNoSounding = _soundingRowBySsrc.empty(); updateRowInSoundingMap( @@ -842,7 +947,8 @@ void Members::Controller::prepare() { } loadMoreRows(); - appendInvitedUsers(); + setupWithAccessUsers(); + setupInvitedUsers(); _prepared = true; setupListChangeViewers(); @@ -893,6 +999,12 @@ void Members::Controller::prepareRows(not_null real) { delegate()->peerListAppendRow(std::move(row)); } } + if (appendWithAccessUsers()) { + changed = true; + } + if (appendInvitedUsers()) { + changed = true; + } if (changed) { delegate()->peerListRefreshRows(); } @@ -1354,8 +1466,9 @@ base::unique_qptr Members::Controller::createRowContextMenu( } const auto canKick = [&] { const auto user = participantPeer->asUser(); - if (static_cast(row.get())->state() - == Row::State::Invited) { + const auto state = static_cast(row.get())->state(); + if (state == Row::State::Invited + || state == Row::State::WithAccess) { return false; } else if (const auto chat = _peer->asChat()) { return chat->amCreator() @@ -1484,6 +1597,7 @@ void Members::Controller::addMuteActionsToContextMenu( const auto muteAction = [&]() -> QAction* { if (muteState == Row::State::Invited + || muteState == Row::State::WithAccess || _call->rtmp() || isMe(participantPeer) || (muteState == Row::State::Inactive @@ -1547,6 +1661,16 @@ std::unique_ptr Members::Controller::createInvitedRow( return result; } +std::unique_ptr Members::Controller::createWithAccessRow( + not_null participantPeer) { + if (findRow(participantPeer)) { + return nullptr; + } + auto result = std::make_unique(this, participantPeer); + updateRow(result.get(), std::nullopt, nullptr, Row::State::WithAccess); + return result; +} + Members::Members( not_null parent, not_null call, diff --git a/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp b/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp index 02ae9cd685..fd91ebbcb0 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members_row.cpp @@ -138,41 +138,52 @@ void MembersRow::setSkipLevelUpdate(bool value) { _skipLevelUpdate = value; } -void MembersRow::updateState( - const Data::GroupCallParticipant *participant) { - setVolume(participant - ? participant->volume - : Group::kDefaultVolume); - if (!participant) { - setState(State::Invited); - setSounding(false); - setSpeaking(false); - _mutedByMe = false; - _raisedHandRating = 0; - } else if (!participant->muted - || (participant->sounding && participant->ssrc != 0) - || (participant->additionalSounding - && GetAdditionalAudioSsrc(participant->videoParams) != 0)) { +void MembersRow::updateStateInvited() { + setVolume(Group::kDefaultVolume); + setState(State::Invited); + setSounding(false); + setSpeaking(false); + _mutedByMe = false; + _raisedHandRating = 0; + refreshStatus(); +} + +void MembersRow::updateStateWithAccess() { + setVolume(Group::kDefaultVolume); + setState(State::WithAccess); + setSounding(false); + setSpeaking(false); + _mutedByMe = false; + _raisedHandRating = 0; + refreshStatus(); +} + +void MembersRow::updateState(const Data::GroupCallParticipant &participant) { + setVolume(participant.volume); + if (!participant.muted + || (participant.sounding && participant.ssrc != 0) + || (participant.additionalSounding + && GetAdditionalAudioSsrc(participant.videoParams) != 0)) { setState(State::Active); - setSounding((participant->sounding && participant->ssrc != 0) - || (participant->additionalSounding - && GetAdditionalAudioSsrc(participant->videoParams) != 0)); - setSpeaking((participant->speaking && participant->ssrc != 0) - || (participant->additionalSpeaking - && GetAdditionalAudioSsrc(participant->videoParams) != 0)); - _mutedByMe = participant->mutedByMe; + setSounding((participant.sounding && participant.ssrc != 0) + || (participant.additionalSounding + && GetAdditionalAudioSsrc(participant.videoParams) != 0)); + setSpeaking((participant.speaking && participant.ssrc != 0) + || (participant.additionalSpeaking + && GetAdditionalAudioSsrc(participant.videoParams) != 0)); + _mutedByMe = participant.mutedByMe; _raisedHandRating = 0; - } else if (participant->canSelfUnmute) { + } else if (participant.canSelfUnmute) { setState(State::Inactive); setSounding(false); setSpeaking(false); - _mutedByMe = participant->mutedByMe; + _mutedByMe = participant.mutedByMe; _raisedHandRating = 0; } else { setSounding(false); setSpeaking(false); - _mutedByMe = participant->mutedByMe; - _raisedHandRating = participant->raisedHandRating; + _mutedByMe = participant.mutedByMe; + _raisedHandRating = participant.raisedHandRating; setState(_raisedHandRating ? State::RaisedHand : State::Muted); } refreshStatus(); @@ -450,6 +461,20 @@ void MembersRow::paintMuteIcon( _delegate->rowPaintIcon(p, iconRect, computeIconState(style)); } +QString MembersRow::generateName() { + const auto result = peer()->name(); + return result.isEmpty() + ? u"User #%1"_q.arg(peerToUser(peer()->id).bare) + : result; +} + +QString MembersRow::generateShortName() { + const auto result = peer()->shortName(); + return result.isEmpty() + ? u"User #%1"_q.arg(peerToUser(peer()->id).bare) + : result; +} + auto MembersRow::generatePaintUserpicCallback(bool forceRound) -> PaintRoundImageCallback { return [=](Painter &p, int x, int y, int outerWidth, int size) { @@ -613,11 +638,13 @@ void MembersRow::paintComplexStatusText( availableWidth -= skip; const auto &font = st::normalFont; const auto useAbout = !_about.isEmpty() + && (_state != State::WithAccess) && (style != MembersRowStyle::Video) && ((_state == State::RaisedHand && !_raisedHandStatus) || (_state != State::RaisedHand && !_speaking)); if (!useAbout && _state != State::Invited + && _state != State::WithAccess && !_mutedByMe) { paintStatusIcon(p, x, y, st, font, selected, narrowMode); @@ -663,6 +690,8 @@ void MembersRow::paintComplexStatusText( ? tr::lng_group_call_muted_by_me_status(tr::now) : _delegate->rowIsMe(peer()) ? tr::lng_status_connecting(tr::now) + : (_state == State::WithAccess) + ? tr::lng_group_call_blockchain_only_status(tr::now) : tr::lng_group_call_invited_status(tr::now))); } } diff --git a/Telegram/SourceFiles/calls/group/calls_group_members_row.h b/Telegram/SourceFiles/calls/group/calls_group_members_row.h index 5ba910c8e2..ef59b2686c 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members_row.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members_row.h @@ -75,11 +75,14 @@ public: Muted, RaisedHand, Invited, + WithAccess, }; void setAbout(const QString &about); void setSkipLevelUpdate(bool value); - void updateState(const Data::GroupCallParticipant *participant); + void updateState(const Data::GroupCallParticipant &participant); + void updateStateInvited(); + void updateStateWithAccess(); void updateLevel(float level); void updateBlobAnimation(crl::time now); void clearRaisedHandStatus(); @@ -122,6 +125,8 @@ public: bool selected, bool actionSelected) override; + QString generateName() override; + QString generateShortName() override; PaintRoundImageCallback generatePaintUserpicCallback( bool forceRound) override; void paintComplexUserpic(