diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index 319c7223ca..933bcb5e67 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -325,8 +325,14 @@ void GroupCall::finish(FinishType type) { } void GroupCall::setMuted(MuteState mute) { + const auto wasMuted = (muted() == MuteState::Muted) + || (muted() == MuteState::PushToTalk); _muted = mute; - applySelfInCallLocally(); + const auto nowMuted = (muted() == MuteState::Muted) + || (muted() == MuteState::PushToTalk); + if (wasMuted != nowMuted) { + applySelfInCallLocally(); + } } void GroupCall::handleUpdate(const MTPGroupCall &call) { diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index fb48433f65..6b190731fe 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -182,6 +182,7 @@ public: MembersController( not_null call, not_null menuParent); + ~MembersController(); using MuteRequest = GroupMembers::MuteRequest; @@ -246,6 +247,7 @@ private: not_null _menuParent; base::unique_qptr _menu; + base::flat_set> _menuCheckRowsAfterHidden; base::flat_map> _speakingRowBySsrc; Ui::Animations::Basic _speakingAnimation; @@ -276,7 +278,8 @@ void Row::updateState(const Data::GroupCall::Participant *participant) { } setState(State::Inactive); setSpeaking(false); - } else if (!participant->muted) { + } else if (!participant->muted + || (participant->speaking && participant->ssrc != 0)) { setState(State::Active); setSpeaking(participant->speaking && participant->ssrc != 0); } else if (participant->canSelfUnmute) { @@ -550,6 +553,13 @@ MembersController::MembersController( }); } +MembersController::~MembersController() { + if (_menu) { + _menu->setDestroyedCallback(nullptr); + _menu = nullptr; + } +} + void MembersController::setupListChangeViewers(not_null call) { const auto channel = call->channel(); channel->session().changes().peerFlagsValue( @@ -638,6 +648,11 @@ void MembersController::updateRow( } void MembersController::checkSpeakingRowPosition(not_null row) { + if (_menu) { + // Don't reorder rows while we show the popup menu. + _menuCheckRowsAfterHidden.emplace(row->peer()); + return; + } // Check if there are non-speaking rows above this one. const auto count = delegate()->peerListFullRowsCount(); for (auto i = 0; i != count; ++i) { @@ -873,11 +888,28 @@ auto MembersController::kickMemberRequests() const void MembersController::rowClicked(not_null row) { if (_menu) { + _menu->setDestroyedCallback(nullptr); _menu->deleteLater(); _menu = nullptr; } _menu = rowContextMenu(_menuParent, row); - _menu->popup(QCursor::pos()); + if (const auto raw = _menu.get()) { + raw->setDestroyedCallback([=] { + if (_menu && _menu.get() != raw) { + return; + } + auto saved = base::take(_menu); + for (const auto peer : base::take(_menuCheckRowsAfterHidden)) { + if (const auto row = findRow(peer->asUser())) { + if (row->speaking()) { + checkSpeakingRowPosition(row); + } + } + } + _menu = std::move(saved); + }); + raw->popup(QCursor::pos()); + } } void MembersController::rowActionClicked( @@ -890,6 +922,9 @@ base::unique_qptr MembersController::rowContextMenu( not_null row) { Expects(row->peer()->isUser()); + if (row->peer()->isSelf()) { + return nullptr; + } const auto real = static_cast(row.get()); const auto user = row->peer()->asUser(); auto result = base::make_unique_q( @@ -951,7 +986,7 @@ base::unique_qptr MembersController::rowContextMenu( _kickMemberRequests.fire_copy(user); }); - if (!user->isSelf() && _channel->canManageCall()) { + if (_channel->canManageCall()) { result->addAction( (mute ? tr::lng_group_call_context_mute(tr::now) diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index aad2b3415f..a5df582cfc 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -207,14 +207,16 @@ void GroupCall::applyParticipantsSlice( const auto was = (i != end(_participants)) ? std::make_optional(*i) : std::nullopt; + const auto canSelfUnmute = !data.is_muted() + || data.is_can_self_unmute(); const auto value = Participant{ .user = user, .date = data.vdate().v, .lastActive = was ? was->lastActive : 0, .ssrc = uint32(data.vsource().v), - .speaking = !data.is_muted() && (was ? was->speaking : false), + .speaking = canSelfUnmute && (was ? was->speaking : false), .muted = data.is_muted(), - .canSelfUnmute = !data.is_muted() || data.is_can_self_unmute(), + .canSelfUnmute = canSelfUnmute, }; if (i == end(_participants)) { _userBySsrc.emplace(value.ssrc, user); @@ -258,7 +260,7 @@ void GroupCall::applyParticipantsMutes( const auto was = *i; i->muted = data.is_muted(); i->canSelfUnmute = !i->muted || data.is_can_self_unmute(); - if (i->muted) { + if (!i->canSelfUnmute) { i->speaking = false; } _participantUpdates.fire({ @@ -280,7 +282,8 @@ void GroupCall::applyLastSpoke(uint32 ssrc, crl::time when, crl::time now) { const auto j = ranges::find(_participants, i->second, &Participant::user); Assert(j != end(_participants)); - const auto speaking = (when + kSpeakStatusKeptFor >= now) && !j->muted; + const auto speaking = (when + kSpeakStatusKeptFor >= now) + && j->canSelfUnmute; if (j->speaking != speaking) { const auto was = *j; j->speaking = speaking;