From a6b4cdd62dccd35d07b792a139e979872be6ca5d Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 27 Nov 2020 17:50:41 +0300 Subject: [PATCH] Improve group call members list updating. --- Telegram/SourceFiles/boxes/peer_list_box.cpp | 12 +- Telegram/SourceFiles/boxes/peer_list_box.h | 3 +- .../SourceFiles/calls/calls_group_call.cpp | 59 ++-- Telegram/SourceFiles/calls/calls_group_call.h | 5 +- .../SourceFiles/calls/calls_group_members.cpp | 253 ++++++++++++------ .../SourceFiles/calls/calls_group_members.h | 5 +- .../SourceFiles/calls/calls_group_panel.cpp | 13 +- .../SourceFiles/calls/calls_group_panel.h | 2 + Telegram/SourceFiles/data/data_group_call.cpp | 116 +++++--- Telegram/SourceFiles/data/data_group_call.h | 26 +- Telegram/ThirdParty/tgcalls | 2 +- 11 files changed, 332 insertions(+), 164 deletions(-) diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 2ed83d917..922a1bdcf 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -426,9 +426,9 @@ bool PeerListRow::checked() const { return _checkbox && _checkbox->checked(); } -void PeerListRow::setCustomStatus(const QString &status) { +void PeerListRow::setCustomStatus(const QString &status, bool active) { setStatusText(status); - _statusType = StatusType::Custom; + _statusType = active ? StatusType::CustomActive : StatusType::Custom; _statusValidTill = 0; } @@ -438,7 +438,10 @@ void PeerListRow::clearCustomStatus() { } void PeerListRow::refreshStatus() { - if (!_initialized || special() || _statusType == StatusType::Custom) { + if (!_initialized + || special() + || _statusType == StatusType::Custom + || _statusType == StatusType::CustomActive) { return; } _statusType = StatusType::LastSeen; @@ -550,7 +553,8 @@ void PeerListRow::paintStatusText( int availableWidth, int outerWidth, bool selected) { - auto statusHasOnlineColor = (_statusType == PeerListRow::StatusType::Online); + auto statusHasOnlineColor = (_statusType == PeerListRow::StatusType::Online) + || (_statusType == PeerListRow::StatusType::CustomActive); p.setFont(st::contactsStatusFont); p.setPen(statusHasOnlineColor ? st.statusFgActive : (selected ? st.statusFgOver : st.statusFg)); _status.drawLeftElided(p, x, y, availableWidth, outerWidth); diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 364ed1e8b..36df9e66c 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -88,7 +88,7 @@ public: [[nodiscard]] virtual auto generatePaintUserpicCallback() -> PaintRoundImageCallback; - void setCustomStatus(const QString &status); + void setCustomStatus(const QString &status, bool active = false); void clearCustomStatus(); // Box interface. @@ -127,6 +127,7 @@ public: Online, LastSeen, Custom, + CustomActive, }; virtual void refreshStatus(); crl::time refreshStatusTime() const; diff --git a/Telegram/SourceFiles/calls/calls_group_call.cpp b/Telegram/SourceFiles/calls/calls_group_call.cpp index e8239940b..4a65adaf1 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/calls_group_call.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "lang/lang_keys.h" #include "boxes/confirm_box.h" +#include "base/unixtime.h" #include "core/application.h" #include "core/core_settings.h" #include "data/data_changes.h" @@ -93,19 +94,21 @@ void GroupCall::start() { void GroupCall::join(const MTPInputGroupCall &inputCall) { setState(State::Joining); + _channel->setCall(inputCall); + inputCall.match([&](const MTPDinputGroupCall &data) { _id = data.vid().v; _accessHash = data.vaccess_hash().v; createAndStartController(); rejoin(); }); - _channel->setCall(inputCall); - _channel->session().changes().peerFlagsValue( - _channel, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::start_with_next([=] { - checkParticipants(); + using Update = Data::GroupCall::ParticipantUpdate; + _channel->call()->participantUpdated( + ) | rpl::filter([=](const Update &update) { + return (_instance != nullptr) && update.removed; + }) | rpl::start_with_next([=](const Update &update) { + _instance->removeSsrcs({ update.participant.source }); }, _lifetime); } @@ -113,6 +116,9 @@ void GroupCall::rejoin() { Expects(_state.current() == State::Joining); _mySsrc = 0; + applySelfInCallLocally(); + LOG(("Call Info: Requesting join payload.")); + const auto weak = base::make_weak(this); _instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) { crl::on_main(weak, [=, payload = std::move(payload)]{ @@ -134,6 +140,9 @@ void GroupCall::rejoin() { root.insert("fingerprints", fingerprints); root.insert("ssrc", double(payload.ssrc)); + LOG(("Call Info: Join payload received, joining with source: %1." + ).arg(ssrc)); + const auto json = QJsonDocument(root).toJson( QJsonDocument::Compact); const auto muted = _muted.current(); @@ -146,6 +155,7 @@ void GroupCall::rejoin() { )).done([=](const MTPUpdates &updates) { _mySsrc = ssrc; setState(State::Joined); + applySelfInCallLocally(); if (_muted.current() != muted) { sendMutedUpdate(); @@ -159,28 +169,28 @@ void GroupCall::rejoin() { }); } -void GroupCall::checkParticipants() { - if (!joined()) { - return; - } +void GroupCall::applySelfInCallLocally() { const auto call = _channel->call(); if (!call || call->id() != _id) { - finish(FinishType::Ended); return; } - const auto &sources = call->sources(); - if (sources.size() != call->fullCount() || sources.empty()) { - call->reload(); - return; - } - auto ssrcs = std::vector(); - ssrcs.reserve(sources.size()); - for (const auto source : sources) { - if (source != _mySsrc) { - ssrcs.push_back(source); - } - } -// _instance->setSsrcs(std::move(ssrcs)); + const auto my = [&] { + const auto self = _channel->session().userId(); + const auto now = base::unixtime::now(); + using Flag = MTPDgroupCallParticipant::Flag; + return MTP_groupCallParticipant( + MTP_flags((_mySsrc ? Flag(0) : Flag::f_left) + | (_muted.current() ? Flag::f_muted : Flag(0))), + MTP_int(self), + MTP_int(now), + MTP_int(0), + MTP_int(_mySsrc)); + }; + call->applyUpdateChecked( + MTP_updateGroupCallParticipants( + inputCall(), + MTP_vector(1, my()), + MTP_int(0)).c_updateGroupCallParticipants()); } void GroupCall::hangup() { @@ -292,7 +302,6 @@ void GroupCall::handleUpdate(const MTPGroupCall &call) { }); } _instance->setJoinResponsePayload(payload); - checkParticipants(); }); } }, [&](const MTPDgroupCallDiscarded &data) { diff --git a/Telegram/SourceFiles/calls/calls_group_call.h b/Telegram/SourceFiles/calls/calls_group_call.h index 6dee42bc8..fe348e678 100644 --- a/Telegram/SourceFiles/calls/calls_group_call.h +++ b/Telegram/SourceFiles/calls/calls_group_call.h @@ -36,6 +36,9 @@ public: const MTPInputGroupCall &inputCall); ~GroupCall(); + [[nodiscard]] uint64 id() const { + return _id; + } [[nodiscard]] not_null channel() const { return _channel; } @@ -92,11 +95,11 @@ private: void handleControllerError(const QString &error); void createAndStartController(); void destroyController(); - void checkParticipants(); void setState(State state); void finish(FinishType type); void sendMutedUpdate(); + void applySelfInCallLocally(); void rejoin(); [[nodiscard]] MTPInputGroupCall inputCall() const; diff --git a/Telegram/SourceFiles/calls/calls_group_members.cpp b/Telegram/SourceFiles/calls/calls_group_members.cpp index 1c851be9f..3edf6f2d4 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/calls_group_members.cpp @@ -24,40 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Calls { namespace { -class MembersController final - : public PeerListController - , public base::has_weak_ptr { -public: - explicit MembersController(not_null call); - - Main::Session &session() const override; - void prepare() override; - void rowClicked(not_null row) override; - void rowActionClicked(not_null row) override; - base::unique_qptr rowContextMenu( - QWidget *parent, - not_null row) override; - void loadMoreRows() override; - -private: - [[nodiscard]] std::unique_ptr createRow( - not_null user) const; - - void prepareRows(); - - void setupListChangeViewers(); - bool appendRow(not_null user); - bool prependRow(not_null user); - bool removeRow(not_null user); - - const base::weak_ptr _call; - const not_null _channel; - - Ui::BoxPointer _addBox; - rpl::lifetime _lifetime; - -}; - class Row final : public PeerListRow { public: Row(not_null channel, not_null user); @@ -68,6 +34,8 @@ public: Muted, }; + void updateState(const Data::GroupCall::Participant *participant); + void addActionRipple(QPoint point, Fn updateCallback) override; void stopLastActionRipple() override; @@ -108,6 +76,50 @@ private: }; +class MembersController final + : public PeerListController + , public base::has_weak_ptr { +public: + explicit MembersController(not_null call); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + void rowActionClicked(not_null row) override; + base::unique_qptr rowContextMenu( + QWidget *parent, + not_null row) override; + void loadMoreRows() override; + + [[nodiscard]] rpl::producer fullCountValue() const { + return _fullCount.value(); + } + +private: + + [[nodiscard]] std::unique_ptr createSelfRow() const; + [[nodiscard]] std::unique_ptr createRow( + const Data::GroupCall::Participant &participant) const; + + void prepareRows(not_null real); + + void setupListChangeViewers(not_null call); + void subscribeToChanges(not_null real); + void updateRow( + const Data::GroupCall::Participant &participant); + void updateRow( + not_null row, + const Data::GroupCall::Participant *participant) const; + + const base::weak_ptr _call; + const not_null _channel; + + rpl::variable _fullCount = 1; + Ui::BoxPointer _addBox; + rpl::lifetime _lifetime; + +}; + Row::Row(not_null channel, not_null user) : PeerListRow(user) , _state(ComputeState(channel, user)) @@ -115,6 +127,24 @@ Row::Row(not_null channel, not_null user) refreshStatus(); } +void Row::updateState(const Data::GroupCall::Participant *participant) { + if (!participant) { + if (peer()->isSelf()) { + setCustomStatus(tr::lng_group_call_connecting(tr::now)); + } else { + setCustomStatus(QString()); + } + _state = State::Inactive; + } else if (!participant->muted) { + _state = State::Active; + } else if (participant->canSelfUnmute) { + _state = State::Inactive; + } else { + _state = State::Muted; + } + _st = ComputeIconStyle(_state); +} + void Row::paintAction( Painter &p, int x, @@ -146,7 +176,7 @@ void Row::refreshStatus() { case State::Active: return tr::lng_group_call_active(tr::now); } Unexpected("State in Row::refreshStatus."); - }()); + }(), (_state == State::Active)); } Row::State Row::ComputeState( @@ -202,18 +232,81 @@ void Row::stopLastActionRipple() { MembersController::MembersController(not_null call) : _call(call) , _channel(call->channel()) { - setupListChangeViewers(); + setupListChangeViewers(call); } -void MembersController::setupListChangeViewers() { - const auto call = _call.get(); +void MembersController::setupListChangeViewers(not_null call) { const auto channel = call->channel(); - channel->session().changes().peerUpdates( + channel->session().changes().peerFlagsValue( channel, Data::PeerUpdate::Flag::GroupCall - ) | rpl::start_with_next([=] { - prepareRows(); + ) | rpl::map([=] { + return channel->call(); + }) | rpl::filter([=](Data::GroupCall *real) { + const auto call = _call.get(); + return call && real && (real->id() == call->id()); + }) | rpl::take( + 1 + ) | rpl::start_with_next([=](not_null real) { + subscribeToChanges(real); }, _lifetime); + + call->stateValue( + ) | rpl::start_with_next([=] { + const auto call = _call.get(); + const auto real = channel->call(); + if (call && real && (real->id() == call->id())) { + //updateRow(channel->session().user()); + } + }, _lifetime); +} + +void MembersController::subscribeToChanges(not_null real) { + _fullCount = real->fullCountValue( + ) | rpl::map([](int value) { + return std::max(value, 1); + }); + + real->participantsSliceAdded( + ) | rpl::start_with_next([=] { + prepareRows(real); + }, _lifetime); + + using Update = Data::GroupCall::ParticipantUpdate; + real->participantUpdated( + ) | rpl::start_with_next([=](const Update &update) { + const auto user = update.participant.user; + if (update.removed) { + if (auto row = delegate()->peerListFindRow(user->id)) { + if (user->isSelf()) { + static_cast(row)->updateState(nullptr); + delegate()->peerListUpdateRow(row); + } else { + delegate()->peerListRemoveRow(row); + delegate()->peerListRefreshRows(); + } + } + } else { + updateRow(update.participant); + } + }, _lifetime); +} + +void MembersController::updateRow( + const Data::GroupCall::Participant &participant) { + if (auto row = delegate()->peerListFindRow(participant.user->id)) { + updateRow(static_cast(row), &participant); + } else if (auto row = createRow(participant)) { + delegate()->peerListAppendRow(std::move(row)); + delegate()->peerListRefreshRows(); + } +} + +void MembersController::updateRow( + not_null row, + const Data::GroupCall::Participant *participant) const { + row->updateState(participant); + delegate()->peerListUpdateRow(row); } Main::Session &MembersController::session() const { @@ -226,17 +319,18 @@ void MembersController::prepare() { setDescriptionText(tr::lng_contacts_loading(tr::now)); setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); - prepareRows(); - delegate()->peerListRefreshRows(); - + const auto call = _call.get(); + if (const auto real = _channel->call(); + real && call && real->id() == call->id()) { + prepareRows(real); + } else if (auto row = createSelfRow()) { + delegate()->peerListAppendRow(std::move(row)); + delegate()->peerListRefreshRows(); + } loadMoreRows(); } -void MembersController::prepareRows() { - const auto real = _channel->call(); - if (!real) { - return; - } +void MembersController::prepareRows(not_null real) { auto foundSelf = false; auto changed = false; const auto &participants = real->participants(); @@ -262,13 +356,19 @@ void MembersController::prepareRows() { } } if (!foundSelf) { - if (auto row = createRow(_channel->session().user())) { + const auto self = _channel->session().user(); + const auto i = ranges::find( + participants, + _channel->session().user(), + &Data::GroupCall::Participant::user); + auto row = (i != end(participants)) ? createRow(*i) : createSelfRow(); + if (row) { changed = true; delegate()->peerListAppendRow(std::move(row)); } } for (const auto &participant : participants) { - if (auto row = createRow(participant.user)) { + if (auto row = createRow(participant)) { changed = true; delegate()->peerListAppendRow(std::move(row)); } @@ -279,10 +379,8 @@ void MembersController::prepareRows() { } void MembersController::loadMoreRows() { - if (const auto call = _call.get()) { - if (const auto real = call->channel()->call()) { - real->requestParticipants(); - } + if (const auto real = _channel->call()) { + real->requestParticipants(); } } @@ -308,33 +406,18 @@ base::unique_qptr MembersController::rowContextMenu( return nullptr; } -bool MembersController::appendRow(not_null user) { - if (delegate()->peerListFindRow(user->id)) { - return false; - } - delegate()->peerListAppendRow(createRow(user)); - return true; -} - -bool MembersController::prependRow(not_null user) { - if (auto row = delegate()->peerListFindRow(user->id)) { - return false; - } - delegate()->peerListPrependRow(createRow(user)); - return true; -} - -bool MembersController::removeRow(not_null user) { - if (auto row = delegate()->peerListFindRow(user->id)) { - delegate()->peerListRemoveRow(row); - return true; - } - return false; +std::unique_ptr MembersController::createSelfRow() const { + const auto self = _channel->session().user(); + auto result = std::make_unique(_channel, self); + updateRow(result.get(), nullptr); + return result; } std::unique_ptr MembersController::createRow( - not_null user) const { - return std::make_unique(_channel, user); + const Data::GroupCall::Participant &participant) const { + auto result = std::make_unique(_channel, participant.user); + updateRow(result.get(), &participant); + return result; } } // namespace @@ -393,19 +476,13 @@ void GroupMembers::setupHeader(not_null call) { object_ptr GroupMembers::setupTitle( not_null call) { - const auto channel = call->channel(); - auto count = channel->session().changes().peerFlagsValue( - channel, - Data::PeerUpdate::Flag::GroupCall - ) | rpl::map([=] { - const auto call = channel->call(); - return std::max(call ? call->fullCount() : 0, 1); - }); + const auto controller = static_cast( + _listController.get()); auto result = object_ptr( _titleWrap, tr::lng_chat_status_members( lt_count_decimal, - std::move(count) | tr::to_count(), + controller->fullCountValue() | tr::to_count(), Ui::Text::Upper ), st::groupCallHeaderLabel); diff --git a/Telegram/SourceFiles/calls/calls_group_members.h b/Telegram/SourceFiles/calls/calls_group_members.h index ff1c46385..7883c6120 100644 --- a/Telegram/SourceFiles/calls/calls_group_members.h +++ b/Telegram/SourceFiles/calls/calls_group_members.h @@ -13,6 +13,10 @@ namespace Ui { class ScrollArea; } // namespace Ui +namespace Data { +class GroupCall; +} // namespace Data + namespace Calls { class GroupCall; @@ -58,7 +62,6 @@ private: void setupButtons(); void addMember(); - void showMembersWithSearch(bool withSearch); void updateHeaderControlsGeometry(int newWidth); base::weak_ptr _call; diff --git a/Telegram/SourceFiles/calls/calls_group_panel.cpp b/Telegram/SourceFiles/calls/calls_group_panel.cpp index fbedf2e0f..5e6cc20f4 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_group_panel.cpp @@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/buttons.h" #include "ui/widgets/window.h" #include "ui/effects/ripple_animation.h" +#include "ui/layers/layer_manager.h" +#include "boxes/confirm_box.h" #include "core/application.h" #include "lang/lang_keys.h" #include "base/event_filter.h" @@ -216,6 +218,7 @@ GroupPanel::GroupPanel(not_null call) : _call(call) , _channel(call->channel()) , _window(std::make_unique(Core::App().getModalParent())) +, _layerBg(std::make_unique(_window->body())) #ifdef Q_OS_WIN , _controls(std::make_unique( _window.get(), @@ -300,9 +303,10 @@ void GroupPanel::initControls() { } }); _hangup->setClickedCallback([=] { - if (_call) { - _call->hangup(); - } + _layerBg->showBox(Box( + tr::lng_group_call_leave_sure(tr::now), + tr::lng_group_call_leave(tr::now), + [=] { if (_call) _call->hangup(); })); }); _settings->setClickedCallback([=] { }); @@ -409,7 +413,7 @@ void GroupPanel::updateControlsGeometry() { - membersTop - st::groupCallMembersMargin.bottom(); _members->setGeometry( - st::groupCallMembersMargin.left(), + (widget()->width() - membersWidth) / 2, membersTop, membersWidth, std::min(desiredHeight, availableHeight)); @@ -426,6 +430,7 @@ void GroupPanel::refreshTitle() { widget(), tr::lng_group_call_title(), st::groupCallHeaderLabel); + _title->setAttribute(Qt::WA_TransparentForMouseEvents); _window->setTitle(u" "_q); } const auto best = _title->naturalWidth(); diff --git a/Telegram/SourceFiles/calls/calls_group_panel.h b/Telegram/SourceFiles/calls/calls_group_panel.h index d4c87dd63..facca9cd4 100644 --- a/Telegram/SourceFiles/calls/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/calls_group_panel.h @@ -30,6 +30,7 @@ template class PaddingWrap; class Window; class ScrollArea; +class LayerManager; namespace Platform { class TitleControls; } // namespace Platform @@ -84,6 +85,7 @@ private: not_null _channel; const std::unique_ptr _window; + const std::unique_ptr _layerBg; #ifdef Q_OS_WIN std::unique_ptr _controls; diff --git a/Telegram/SourceFiles/data/data_group_call.cpp b/Telegram/SourceFiles/data/data_group_call.cpp index 87748b3d1..1d6265614 100644 --- a/Telegram/SourceFiles/data/data_group_call.cpp +++ b/Telegram/SourceFiles/data/data_group_call.cpp @@ -51,14 +51,10 @@ auto GroupCall::participants() const return _participants; } -const base::flat_set &GroupCall::sources() const { - return _sources; -} - void GroupCall::requestParticipants() { if (_participantsRequestId || _reloadRequestId) { return; - } else if (_participants.size() >= _fullCount && _allReceived) { + } else if (_participants.size() >= _fullCount.current() && _allReceived) { return; } else if (_allReceived) { reload(); @@ -83,9 +79,7 @@ void GroupCall::requestParticipants() { _fullCount = _participants.size(); } }); - _channel->session().changes().peerUpdated( - _channel, - PeerUpdate::Flag::GroupCall); + _participantsSliceAdded.fire({}); _participantsRequestId = 0; }).fail([=](const RPCError &error) { _fullCount = _participants.size(); @@ -98,13 +92,26 @@ void GroupCall::requestParticipants() { } int GroupCall::fullCount() const { - return _fullCount; + return _fullCount.current(); +} + +rpl::producer GroupCall::fullCountValue() const { + return _fullCount.value(); } bool GroupCall::participantsLoaded() const { return _allReceived; } +rpl::producer<> GroupCall::participantsSliceAdded() { + return _participantsSliceAdded.events(); +} + +auto GroupCall::participantUpdated() const +-> rpl::producer { + return _participantUpdates.events(); +} + void GroupCall::applyUpdate(const MTPGroupCall &update) { applyCall(update, false); } @@ -112,7 +119,7 @@ void GroupCall::applyUpdate(const MTPGroupCall &update) { void GroupCall::applyCall(const MTPGroupCall &call, bool force) { call.match([&](const MTPDgroupCall &data) { const auto changed = (_version != data.vversion().v) - || (_fullCount != data.vparticipants_count().v); + || (_fullCount.current() != data.vparticipants_count().v); if (!force && !changed) { return; } else if (!force && _version > data.vversion().v) { @@ -130,9 +137,6 @@ void GroupCall::applyCall(const MTPGroupCall &call, bool force) { _finished = true; _duration = data.vduration().v; }); - _channel->session().changes().peerUpdated( - _channel, - PeerUpdate::Flag::GroupCall); } void GroupCall::reload() { @@ -148,17 +152,10 @@ void GroupCall::reload() { result.match([&](const MTPDphone_groupCall &data) { _channel->owner().processUsers(data.vusers()); _participants.clear(); - _sources.clear(); applyParticipantsSlice(data.vparticipants().v); - for (const auto &source : data.vsources().v) { - _sources.emplace(source.v); - } - _fullCount = _sources.size(); - if (_participants.size() > _fullCount) { - _fullCount = _participants.size(); - } - _allReceived = (_fullCount == _participants.size()); applyCall(data.vcall(), true); + _allReceived = (_fullCount.current() == _participants.size()); + _participantsSliceAdded.fire({}); }); _reloadRequestId = 0; }).fail([=](const RPCError &error) { @@ -167,7 +164,9 @@ void GroupCall::reload() { } void GroupCall::applyParticipantsSlice( - const QVector &list) { + const QVector &list, + bool sendIndividualUpdates) { + auto fullCount = _fullCount.current(); for (const auto &participant : list) { participant.match([&](const MTPDgroupCallParticipant &data) { const auto userId = data.vuser_id().v; @@ -178,11 +177,17 @@ void GroupCall::applyParticipantsSlice( &Participant::user); if (data.is_left()) { if (i != end(_participants)) { - _sources.remove(i->source); + auto update = ParticipantUpdate{ + .participant = *i, + .removed = true, + }; _participants.erase(i); + if (sendIndividualUpdates) { + _participantUpdates.fire(std::move(update)); + } } - if (_fullCount > _participants.size()) { - --_fullCount; + if (fullCount > _participants.size()) { + --fullCount; } return; } @@ -195,28 +200,69 @@ void GroupCall::applyParticipantsSlice( }; if (i == end(_participants)) { _participants.push_back(value); - ++_fullCount; + ++fullCount; } else { *i = value; } - _sources.emplace(uint32(data.vsource().v)); + _participantUpdates.fire({ + .participant = value, + }); }); } - ranges::sort(_participants, std::greater<>(), &Participant::date); + ranges::sort(_participants, std::greater<>(), [](const Participant &p) { + return p.lastActivePrecise + ? p.lastActivePrecise + : p.lastActive + ? p.lastActive + : p.date; + }); + _fullCount = fullCount; +} + +void GroupCall::applyParticipantsMutes( + const MTPDupdateGroupCallParticipants &update) { + for (const auto &participant : update.vparticipants().v) { + participant.match([&](const MTPDgroupCallParticipant &data) { + if (data.is_left()) { + return; + } + const auto userId = data.vuser_id().v; + const auto user = _channel->owner().user(userId); + const auto i = ranges::find( + _participants, + user, + &Participant::user); + if (i != end(_participants)) { + i->muted = data.is_muted(); + i->canSelfUnmute = data.is_can_self_unmute(); + _participantUpdates.fire({ + .participant = *i, + }); + } + }); + } + } void GroupCall::applyUpdate(const MTPDupdateGroupCallParticipants &update) { - if (update.vversion().v <= _version) { + const auto version = update.vversion().v; + if (version < _version) { return; - } else if (update.vversion().v != _version + 1) { + } else if (version == _version) { + applyParticipantsMutes(update); + return; + } else if (version != _version + 1) { + applyParticipantsMutes(update); reload(); return; } _version = update.vversion().v; - applyParticipantsSlice(update.vparticipants().v); - _channel->session().changes().peerUpdated( - _channel, - PeerUpdate::Flag::GroupCall); + applyUpdateChecked(update); +} + +void GroupCall::applyUpdateChecked( + const MTPDupdateGroupCallParticipants &update) { + applyParticipantsSlice(update.vparticipants().v, true); } } // namespace Data diff --git a/Telegram/SourceFiles/data/data_group_call.h b/Telegram/SourceFiles/data/data_group_call.h index 11c69c4fc..8182abfb8 100644 --- a/Telegram/SourceFiles/data/data_group_call.h +++ b/Telegram/SourceFiles/data/data_group_call.h @@ -24,21 +24,32 @@ public: struct Participant { not_null user; TimeId date = 0; + TimeId lastActive = 0; + TimeId lastActivePrecise = 0; uint32 source = 0; bool muted = false; bool canSelfUnmute = false; }; + struct ParticipantUpdate { + Participant participant; + bool removed = false; + }; [[nodiscard]] auto participants() const -> const std::vector &; - [[nodiscard]] const base::flat_set &sources() const; void requestParticipants(); [[nodiscard]] bool participantsLoaded() const; + [[nodiscard]] rpl::producer<> participantsSliceAdded(); + [[nodiscard]] rpl::producer participantUpdated() const; + void applyUpdate(const MTPGroupCall &update); void applyUpdate(const MTPDupdateGroupCallParticipants &update); + void applyUpdateChecked( + const MTPDupdateGroupCallParticipants &update); [[nodiscard]] int fullCount() const; + [[nodiscard]] rpl::producer fullCountValue() const; void reload(); [[nodiscard]] bool finished() const; @@ -46,7 +57,11 @@ public: private: void applyCall(const MTPGroupCall &call, bool force); - void applyParticipantsSlice(const QVector &list); + void applyParticipantsSlice( + const QVector &list, + bool sendIndividualUpdates = false); + void applyParticipantsMutes( + const MTPDupdateGroupCallParticipants &update); const not_null _channel; const uint64 _id = 0; @@ -57,9 +72,12 @@ private: mtpRequestId _reloadRequestId = 0; std::vector _participants; - base::flat_set _sources; QString _nextOffset; - int _fullCount = 0; + rpl::variable _fullCount = 0; + + rpl::event_stream _participantUpdates; + rpl::event_stream<> _participantsSliceAdded; + int _duration = 0; bool _finished = false; bool _allReceived = false; diff --git a/Telegram/ThirdParty/tgcalls b/Telegram/ThirdParty/tgcalls index d2c6ad40d..ac86c0ee8 160000 --- a/Telegram/ThirdParty/tgcalls +++ b/Telegram/ThirdParty/tgcalls @@ -1 +1 @@ -Subproject commit d2c6ad40d717e604859589d854b81229abd11763 +Subproject commit ac86c0ee86293d6fced7dcb48a8c93657f18a7f3