Improve group call members list updating.

This commit is contained in:
John Preston 2020-11-27 17:50:41 +03:00
parent 8618fe41ee
commit a6b4cdd62d
11 changed files with 332 additions and 164 deletions

View file

@ -426,9 +426,9 @@ bool PeerListRow::checked() const {
return _checkbox && _checkbox->checked(); return _checkbox && _checkbox->checked();
} }
void PeerListRow::setCustomStatus(const QString &status) { void PeerListRow::setCustomStatus(const QString &status, bool active) {
setStatusText(status); setStatusText(status);
_statusType = StatusType::Custom; _statusType = active ? StatusType::CustomActive : StatusType::Custom;
_statusValidTill = 0; _statusValidTill = 0;
} }
@ -438,7 +438,10 @@ void PeerListRow::clearCustomStatus() {
} }
void PeerListRow::refreshStatus() { void PeerListRow::refreshStatus() {
if (!_initialized || special() || _statusType == StatusType::Custom) { if (!_initialized
|| special()
|| _statusType == StatusType::Custom
|| _statusType == StatusType::CustomActive) {
return; return;
} }
_statusType = StatusType::LastSeen; _statusType = StatusType::LastSeen;
@ -550,7 +553,8 @@ void PeerListRow::paintStatusText(
int availableWidth, int availableWidth,
int outerWidth, int outerWidth,
bool selected) { bool selected) {
auto statusHasOnlineColor = (_statusType == PeerListRow::StatusType::Online); auto statusHasOnlineColor = (_statusType == PeerListRow::StatusType::Online)
|| (_statusType == PeerListRow::StatusType::CustomActive);
p.setFont(st::contactsStatusFont); p.setFont(st::contactsStatusFont);
p.setPen(statusHasOnlineColor ? st.statusFgActive : (selected ? st.statusFgOver : st.statusFg)); p.setPen(statusHasOnlineColor ? st.statusFgActive : (selected ? st.statusFgOver : st.statusFg));
_status.drawLeftElided(p, x, y, availableWidth, outerWidth); _status.drawLeftElided(p, x, y, availableWidth, outerWidth);

View file

@ -88,7 +88,7 @@ public:
[[nodiscard]] virtual auto generatePaintUserpicCallback() [[nodiscard]] virtual auto generatePaintUserpicCallback()
-> PaintRoundImageCallback; -> PaintRoundImageCallback;
void setCustomStatus(const QString &status); void setCustomStatus(const QString &status, bool active = false);
void clearCustomStatus(); void clearCustomStatus();
// Box interface. // Box interface.
@ -127,6 +127,7 @@ public:
Online, Online,
LastSeen, LastSeen,
Custom, Custom,
CustomActive,
}; };
virtual void refreshStatus(); virtual void refreshStatus();
crl::time refreshStatusTime() const; crl::time refreshStatusTime() const;

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "base/unixtime.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "data/data_changes.h" #include "data/data_changes.h"
@ -93,19 +94,21 @@ void GroupCall::start() {
void GroupCall::join(const MTPInputGroupCall &inputCall) { void GroupCall::join(const MTPInputGroupCall &inputCall) {
setState(State::Joining); setState(State::Joining);
_channel->setCall(inputCall);
inputCall.match([&](const MTPDinputGroupCall &data) { inputCall.match([&](const MTPDinputGroupCall &data) {
_id = data.vid().v; _id = data.vid().v;
_accessHash = data.vaccess_hash().v; _accessHash = data.vaccess_hash().v;
createAndStartController(); createAndStartController();
rejoin(); rejoin();
}); });
_channel->setCall(inputCall);
_channel->session().changes().peerFlagsValue( using Update = Data::GroupCall::ParticipantUpdate;
_channel, _channel->call()->participantUpdated(
Data::PeerUpdate::Flag::GroupCall ) | rpl::filter([=](const Update &update) {
) | rpl::start_with_next([=] { return (_instance != nullptr) && update.removed;
checkParticipants(); }) | rpl::start_with_next([=](const Update &update) {
_instance->removeSsrcs({ update.participant.source });
}, _lifetime); }, _lifetime);
} }
@ -113,6 +116,9 @@ void GroupCall::rejoin() {
Expects(_state.current() == State::Joining); Expects(_state.current() == State::Joining);
_mySsrc = 0; _mySsrc = 0;
applySelfInCallLocally();
LOG(("Call Info: Requesting join payload."));
const auto weak = base::make_weak(this); const auto weak = base::make_weak(this);
_instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) { _instance->emitJoinPayload([=](tgcalls::GroupJoinPayload payload) {
crl::on_main(weak, [=, payload = std::move(payload)]{ crl::on_main(weak, [=, payload = std::move(payload)]{
@ -134,6 +140,9 @@ void GroupCall::rejoin() {
root.insert("fingerprints", fingerprints); root.insert("fingerprints", fingerprints);
root.insert("ssrc", double(payload.ssrc)); root.insert("ssrc", double(payload.ssrc));
LOG(("Call Info: Join payload received, joining with source: %1."
).arg(ssrc));
const auto json = QJsonDocument(root).toJson( const auto json = QJsonDocument(root).toJson(
QJsonDocument::Compact); QJsonDocument::Compact);
const auto muted = _muted.current(); const auto muted = _muted.current();
@ -146,6 +155,7 @@ void GroupCall::rejoin() {
)).done([=](const MTPUpdates &updates) { )).done([=](const MTPUpdates &updates) {
_mySsrc = ssrc; _mySsrc = ssrc;
setState(State::Joined); setState(State::Joined);
applySelfInCallLocally();
if (_muted.current() != muted) { if (_muted.current() != muted) {
sendMutedUpdate(); sendMutedUpdate();
@ -159,28 +169,28 @@ void GroupCall::rejoin() {
}); });
} }
void GroupCall::checkParticipants() { void GroupCall::applySelfInCallLocally() {
if (!joined()) {
return;
}
const auto call = _channel->call(); const auto call = _channel->call();
if (!call || call->id() != _id) { if (!call || call->id() != _id) {
finish(FinishType::Ended);
return; return;
} }
const auto &sources = call->sources(); const auto my = [&] {
if (sources.size() != call->fullCount() || sources.empty()) { const auto self = _channel->session().userId();
call->reload(); const auto now = base::unixtime::now();
return; using Flag = MTPDgroupCallParticipant::Flag;
} return MTP_groupCallParticipant(
auto ssrcs = std::vector<uint32_t>(); MTP_flags((_mySsrc ? Flag(0) : Flag::f_left)
ssrcs.reserve(sources.size()); | (_muted.current() ? Flag::f_muted : Flag(0))),
for (const auto source : sources) { MTP_int(self),
if (source != _mySsrc) { MTP_int(now),
ssrcs.push_back(source); MTP_int(0),
} MTP_int(_mySsrc));
} };
// _instance->setSsrcs(std::move(ssrcs)); call->applyUpdateChecked(
MTP_updateGroupCallParticipants(
inputCall(),
MTP_vector<MTPGroupCallParticipant>(1, my()),
MTP_int(0)).c_updateGroupCallParticipants());
} }
void GroupCall::hangup() { void GroupCall::hangup() {
@ -292,7 +302,6 @@ void GroupCall::handleUpdate(const MTPGroupCall &call) {
}); });
} }
_instance->setJoinResponsePayload(payload); _instance->setJoinResponsePayload(payload);
checkParticipants();
}); });
} }
}, [&](const MTPDgroupCallDiscarded &data) { }, [&](const MTPDgroupCallDiscarded &data) {

View file

@ -36,6 +36,9 @@ public:
const MTPInputGroupCall &inputCall); const MTPInputGroupCall &inputCall);
~GroupCall(); ~GroupCall();
[[nodiscard]] uint64 id() const {
return _id;
}
[[nodiscard]] not_null<ChannelData*> channel() const { [[nodiscard]] not_null<ChannelData*> channel() const {
return _channel; return _channel;
} }
@ -92,11 +95,11 @@ private:
void handleControllerError(const QString &error); void handleControllerError(const QString &error);
void createAndStartController(); void createAndStartController();
void destroyController(); void destroyController();
void checkParticipants();
void setState(State state); void setState(State state);
void finish(FinishType type); void finish(FinishType type);
void sendMutedUpdate(); void sendMutedUpdate();
void applySelfInCallLocally();
void rejoin(); void rejoin();
[[nodiscard]] MTPInputGroupCall inputCall() const; [[nodiscard]] MTPInputGroupCall inputCall() const;

View file

@ -24,40 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Calls { namespace Calls {
namespace { namespace {
class MembersController final
: public PeerListController
, public base::has_weak_ptr {
public:
explicit MembersController(not_null<GroupCall*> call);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowActionClicked(not_null<PeerListRow*> row) override;
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) override;
void loadMoreRows() override;
private:
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(
not_null<UserData*> user) const;
void prepareRows();
void setupListChangeViewers();
bool appendRow(not_null<UserData*> user);
bool prependRow(not_null<UserData*> user);
bool removeRow(not_null<UserData*> user);
const base::weak_ptr<GroupCall> _call;
const not_null<ChannelData*> _channel;
Ui::BoxPointer _addBox;
rpl::lifetime _lifetime;
};
class Row final : public PeerListRow { class Row final : public PeerListRow {
public: public:
Row(not_null<ChannelData*> channel, not_null<UserData*> user); Row(not_null<ChannelData*> channel, not_null<UserData*> user);
@ -68,6 +34,8 @@ public:
Muted, Muted,
}; };
void updateState(const Data::GroupCall::Participant *participant);
void addActionRipple(QPoint point, Fn<void()> updateCallback) override; void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
void stopLastActionRipple() override; void stopLastActionRipple() override;
@ -108,6 +76,50 @@ private:
}; };
class MembersController final
: public PeerListController
, public base::has_weak_ptr {
public:
explicit MembersController(not_null<GroupCall*> call);
Main::Session &session() const override;
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void rowActionClicked(not_null<PeerListRow*> row) override;
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) override;
void loadMoreRows() override;
[[nodiscard]] rpl::producer<int> fullCountValue() const {
return _fullCount.value();
}
private:
[[nodiscard]] std::unique_ptr<PeerListRow> createSelfRow() const;
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(
const Data::GroupCall::Participant &participant) const;
void prepareRows(not_null<Data::GroupCall*> real);
void setupListChangeViewers(not_null<GroupCall*> call);
void subscribeToChanges(not_null<Data::GroupCall*> real);
void updateRow(
const Data::GroupCall::Participant &participant);
void updateRow(
not_null<Row*> row,
const Data::GroupCall::Participant *participant) const;
const base::weak_ptr<GroupCall> _call;
const not_null<ChannelData*> _channel;
rpl::variable<int> _fullCount = 1;
Ui::BoxPointer _addBox;
rpl::lifetime _lifetime;
};
Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user) Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user)
: PeerListRow(user) : PeerListRow(user)
, _state(ComputeState(channel, user)) , _state(ComputeState(channel, user))
@ -115,6 +127,24 @@ Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user)
refreshStatus(); 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( void Row::paintAction(
Painter &p, Painter &p,
int x, int x,
@ -146,7 +176,7 @@ void Row::refreshStatus() {
case State::Active: return tr::lng_group_call_active(tr::now); case State::Active: return tr::lng_group_call_active(tr::now);
} }
Unexpected("State in Row::refreshStatus."); Unexpected("State in Row::refreshStatus.");
}()); }(), (_state == State::Active));
} }
Row::State Row::ComputeState( Row::State Row::ComputeState(
@ -202,18 +232,81 @@ void Row::stopLastActionRipple() {
MembersController::MembersController(not_null<GroupCall*> call) MembersController::MembersController(not_null<GroupCall*> call)
: _call(call) : _call(call)
, _channel(call->channel()) { , _channel(call->channel()) {
setupListChangeViewers(); setupListChangeViewers(call);
} }
void MembersController::setupListChangeViewers() { void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
const auto call = _call.get();
const auto channel = call->channel(); const auto channel = call->channel();
channel->session().changes().peerUpdates( channel->session().changes().peerFlagsValue(
channel, channel,
Data::PeerUpdate::Flag::GroupCall Data::PeerUpdate::Flag::GroupCall
) | rpl::start_with_next([=] { ) | rpl::map([=] {
prepareRows(); 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<Data::GroupCall*> real) {
subscribeToChanges(real);
}, _lifetime); }, _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<Data::GroupCall*> 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*>(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*>(row), &participant);
} else if (auto row = createRow(participant)) {
delegate()->peerListAppendRow(std::move(row));
delegate()->peerListRefreshRows();
}
}
void MembersController::updateRow(
not_null<Row*> row,
const Data::GroupCall::Participant *participant) const {
row->updateState(participant);
delegate()->peerListUpdateRow(row);
} }
Main::Session &MembersController::session() const { Main::Session &MembersController::session() const {
@ -226,17 +319,18 @@ void MembersController::prepare() {
setDescriptionText(tr::lng_contacts_loading(tr::now)); setDescriptionText(tr::lng_contacts_loading(tr::now));
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now)); setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
prepareRows(); const auto call = _call.get();
delegate()->peerListRefreshRows(); 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(); loadMoreRows();
} }
void MembersController::prepareRows() { void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
const auto real = _channel->call();
if (!real) {
return;
}
auto foundSelf = false; auto foundSelf = false;
auto changed = false; auto changed = false;
const auto &participants = real->participants(); const auto &participants = real->participants();
@ -262,13 +356,19 @@ void MembersController::prepareRows() {
} }
} }
if (!foundSelf) { 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; changed = true;
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
} }
} }
for (const auto &participant : participants) { for (const auto &participant : participants) {
if (auto row = createRow(participant.user)) { if (auto row = createRow(participant)) {
changed = true; changed = true;
delegate()->peerListAppendRow(std::move(row)); delegate()->peerListAppendRow(std::move(row));
} }
@ -279,10 +379,8 @@ void MembersController::prepareRows() {
} }
void MembersController::loadMoreRows() { void MembersController::loadMoreRows() {
if (const auto call = _call.get()) { if (const auto real = _channel->call()) {
if (const auto real = call->channel()->call()) { real->requestParticipants();
real->requestParticipants();
}
} }
} }
@ -308,33 +406,18 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
return nullptr; return nullptr;
} }
bool MembersController::appendRow(not_null<UserData*> user) { std::unique_ptr<PeerListRow> MembersController::createSelfRow() const {
if (delegate()->peerListFindRow(user->id)) { const auto self = _channel->session().user();
return false; auto result = std::make_unique<Row>(_channel, self);
} updateRow(result.get(), nullptr);
delegate()->peerListAppendRow(createRow(user)); return result;
return true;
}
bool MembersController::prependRow(not_null<UserData*> user) {
if (auto row = delegate()->peerListFindRow(user->id)) {
return false;
}
delegate()->peerListPrependRow(createRow(user));
return true;
}
bool MembersController::removeRow(not_null<UserData*> user) {
if (auto row = delegate()->peerListFindRow(user->id)) {
delegate()->peerListRemoveRow(row);
return true;
}
return false;
} }
std::unique_ptr<PeerListRow> MembersController::createRow( std::unique_ptr<PeerListRow> MembersController::createRow(
not_null<UserData*> user) const { const Data::GroupCall::Participant &participant) const {
return std::make_unique<Row>(_channel, user); auto result = std::make_unique<Row>(_channel, participant.user);
updateRow(result.get(), &participant);
return result;
} }
} // namespace } // namespace
@ -393,19 +476,13 @@ void GroupMembers::setupHeader(not_null<GroupCall*> call) {
object_ptr<Ui::FlatLabel> GroupMembers::setupTitle( object_ptr<Ui::FlatLabel> GroupMembers::setupTitle(
not_null<GroupCall*> call) { not_null<GroupCall*> call) {
const auto channel = call->channel(); const auto controller = static_cast<MembersController*>(
auto count = channel->session().changes().peerFlagsValue( _listController.get());
channel,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
const auto call = channel->call();
return std::max(call ? call->fullCount() : 0, 1);
});
auto result = object_ptr<Ui::FlatLabel>( auto result = object_ptr<Ui::FlatLabel>(
_titleWrap, _titleWrap,
tr::lng_chat_status_members( tr::lng_chat_status_members(
lt_count_decimal, lt_count_decimal,
std::move(count) | tr::to_count(), controller->fullCountValue() | tr::to_count(),
Ui::Text::Upper Ui::Text::Upper
), ),
st::groupCallHeaderLabel); st::groupCallHeaderLabel);

View file

@ -13,6 +13,10 @@ namespace Ui {
class ScrollArea; class ScrollArea;
} // namespace Ui } // namespace Ui
namespace Data {
class GroupCall;
} // namespace Data
namespace Calls { namespace Calls {
class GroupCall; class GroupCall;
@ -58,7 +62,6 @@ private:
void setupButtons(); void setupButtons();
void addMember(); void addMember();
void showMembersWithSearch(bool withSearch);
void updateHeaderControlsGeometry(int newWidth); void updateHeaderControlsGeometry(int newWidth);
base::weak_ptr<GroupCall> _call; base::weak_ptr<GroupCall> _call;

View file

@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/window.h" #include "ui/widgets/window.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
#include "ui/layers/layer_manager.h"
#include "boxes/confirm_box.h"
#include "core/application.h" #include "core/application.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "base/event_filter.h" #include "base/event_filter.h"
@ -216,6 +218,7 @@ GroupPanel::GroupPanel(not_null<GroupCall*> call)
: _call(call) : _call(call)
, _channel(call->channel()) , _channel(call->channel())
, _window(std::make_unique<Ui::Window>(Core::App().getModalParent())) , _window(std::make_unique<Ui::Window>(Core::App().getModalParent()))
, _layerBg(std::make_unique<Ui::LayerManager>(_window->body()))
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
, _controls(std::make_unique<Ui::Platform::TitleControls>( , _controls(std::make_unique<Ui::Platform::TitleControls>(
_window.get(), _window.get(),
@ -300,9 +303,10 @@ void GroupPanel::initControls() {
} }
}); });
_hangup->setClickedCallback([=] { _hangup->setClickedCallback([=] {
if (_call) { _layerBg->showBox(Box<ConfirmBox>(
_call->hangup(); tr::lng_group_call_leave_sure(tr::now),
} tr::lng_group_call_leave(tr::now),
[=] { if (_call) _call->hangup(); }));
}); });
_settings->setClickedCallback([=] { _settings->setClickedCallback([=] {
}); });
@ -409,7 +413,7 @@ void GroupPanel::updateControlsGeometry() {
- membersTop - membersTop
- st::groupCallMembersMargin.bottom(); - st::groupCallMembersMargin.bottom();
_members->setGeometry( _members->setGeometry(
st::groupCallMembersMargin.left(), (widget()->width() - membersWidth) / 2,
membersTop, membersTop,
membersWidth, membersWidth,
std::min(desiredHeight, availableHeight)); std::min(desiredHeight, availableHeight));
@ -426,6 +430,7 @@ void GroupPanel::refreshTitle() {
widget(), widget(),
tr::lng_group_call_title(), tr::lng_group_call_title(),
st::groupCallHeaderLabel); st::groupCallHeaderLabel);
_title->setAttribute(Qt::WA_TransparentForMouseEvents);
_window->setTitle(u" "_q); _window->setTitle(u" "_q);
} }
const auto best = _title->naturalWidth(); const auto best = _title->naturalWidth();

View file

@ -30,6 +30,7 @@ template <typename Widget>
class PaddingWrap; class PaddingWrap;
class Window; class Window;
class ScrollArea; class ScrollArea;
class LayerManager;
namespace Platform { namespace Platform {
class TitleControls; class TitleControls;
} // namespace Platform } // namespace Platform
@ -84,6 +85,7 @@ private:
not_null<ChannelData*> _channel; not_null<ChannelData*> _channel;
const std::unique_ptr<Ui::Window> _window; const std::unique_ptr<Ui::Window> _window;
const std::unique_ptr<Ui::LayerManager> _layerBg;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
std::unique_ptr<Ui::Platform::TitleControls> _controls; std::unique_ptr<Ui::Platform::TitleControls> _controls;

View file

@ -51,14 +51,10 @@ auto GroupCall::participants() const
return _participants; return _participants;
} }
const base::flat_set<uint32> &GroupCall::sources() const {
return _sources;
}
void GroupCall::requestParticipants() { void GroupCall::requestParticipants() {
if (_participantsRequestId || _reloadRequestId) { if (_participantsRequestId || _reloadRequestId) {
return; return;
} else if (_participants.size() >= _fullCount && _allReceived) { } else if (_participants.size() >= _fullCount.current() && _allReceived) {
return; return;
} else if (_allReceived) { } else if (_allReceived) {
reload(); reload();
@ -83,9 +79,7 @@ void GroupCall::requestParticipants() {
_fullCount = _participants.size(); _fullCount = _participants.size();
} }
}); });
_channel->session().changes().peerUpdated( _participantsSliceAdded.fire({});
_channel,
PeerUpdate::Flag::GroupCall);
_participantsRequestId = 0; _participantsRequestId = 0;
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
_fullCount = _participants.size(); _fullCount = _participants.size();
@ -98,13 +92,26 @@ void GroupCall::requestParticipants() {
} }
int GroupCall::fullCount() const { int GroupCall::fullCount() const {
return _fullCount; return _fullCount.current();
}
rpl::producer<int> GroupCall::fullCountValue() const {
return _fullCount.value();
} }
bool GroupCall::participantsLoaded() const { bool GroupCall::participantsLoaded() const {
return _allReceived; return _allReceived;
} }
rpl::producer<> GroupCall::participantsSliceAdded() {
return _participantsSliceAdded.events();
}
auto GroupCall::participantUpdated() const
-> rpl::producer<ParticipantUpdate> {
return _participantUpdates.events();
}
void GroupCall::applyUpdate(const MTPGroupCall &update) { void GroupCall::applyUpdate(const MTPGroupCall &update) {
applyCall(update, false); applyCall(update, false);
} }
@ -112,7 +119,7 @@ void GroupCall::applyUpdate(const MTPGroupCall &update) {
void GroupCall::applyCall(const MTPGroupCall &call, bool force) { void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
call.match([&](const MTPDgroupCall &data) { call.match([&](const MTPDgroupCall &data) {
const auto changed = (_version != data.vversion().v) const auto changed = (_version != data.vversion().v)
|| (_fullCount != data.vparticipants_count().v); || (_fullCount.current() != data.vparticipants_count().v);
if (!force && !changed) { if (!force && !changed) {
return; return;
} else if (!force && _version > data.vversion().v) { } else if (!force && _version > data.vversion().v) {
@ -130,9 +137,6 @@ void GroupCall::applyCall(const MTPGroupCall &call, bool force) {
_finished = true; _finished = true;
_duration = data.vduration().v; _duration = data.vduration().v;
}); });
_channel->session().changes().peerUpdated(
_channel,
PeerUpdate::Flag::GroupCall);
} }
void GroupCall::reload() { void GroupCall::reload() {
@ -148,17 +152,10 @@ void GroupCall::reload() {
result.match([&](const MTPDphone_groupCall &data) { result.match([&](const MTPDphone_groupCall &data) {
_channel->owner().processUsers(data.vusers()); _channel->owner().processUsers(data.vusers());
_participants.clear(); _participants.clear();
_sources.clear();
applyParticipantsSlice(data.vparticipants().v); 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); applyCall(data.vcall(), true);
_allReceived = (_fullCount.current() == _participants.size());
_participantsSliceAdded.fire({});
}); });
_reloadRequestId = 0; _reloadRequestId = 0;
}).fail([=](const RPCError &error) { }).fail([=](const RPCError &error) {
@ -167,7 +164,9 @@ void GroupCall::reload() {
} }
void GroupCall::applyParticipantsSlice( void GroupCall::applyParticipantsSlice(
const QVector<MTPGroupCallParticipant> &list) { const QVector<MTPGroupCallParticipant> &list,
bool sendIndividualUpdates) {
auto fullCount = _fullCount.current();
for (const auto &participant : list) { for (const auto &participant : list) {
participant.match([&](const MTPDgroupCallParticipant &data) { participant.match([&](const MTPDgroupCallParticipant &data) {
const auto userId = data.vuser_id().v; const auto userId = data.vuser_id().v;
@ -178,11 +177,17 @@ void GroupCall::applyParticipantsSlice(
&Participant::user); &Participant::user);
if (data.is_left()) { if (data.is_left()) {
if (i != end(_participants)) { if (i != end(_participants)) {
_sources.remove(i->source); auto update = ParticipantUpdate{
.participant = *i,
.removed = true,
};
_participants.erase(i); _participants.erase(i);
if (sendIndividualUpdates) {
_participantUpdates.fire(std::move(update));
}
} }
if (_fullCount > _participants.size()) { if (fullCount > _participants.size()) {
--_fullCount; --fullCount;
} }
return; return;
} }
@ -195,28 +200,69 @@ void GroupCall::applyParticipantsSlice(
}; };
if (i == end(_participants)) { if (i == end(_participants)) {
_participants.push_back(value); _participants.push_back(value);
++_fullCount; ++fullCount;
} else { } else {
*i = value; *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) { void GroupCall::applyUpdate(const MTPDupdateGroupCallParticipants &update) {
if (update.vversion().v <= _version) { const auto version = update.vversion().v;
if (version < _version) {
return; return;
} else if (update.vversion().v != _version + 1) { } else if (version == _version) {
applyParticipantsMutes(update);
return;
} else if (version != _version + 1) {
applyParticipantsMutes(update);
reload(); reload();
return; return;
} }
_version = update.vversion().v; _version = update.vversion().v;
applyParticipantsSlice(update.vparticipants().v); applyUpdateChecked(update);
_channel->session().changes().peerUpdated( }
_channel,
PeerUpdate::Flag::GroupCall); void GroupCall::applyUpdateChecked(
const MTPDupdateGroupCallParticipants &update) {
applyParticipantsSlice(update.vparticipants().v, true);
} }
} // namespace Data } // namespace Data

View file

@ -24,21 +24,32 @@ public:
struct Participant { struct Participant {
not_null<UserData*> user; not_null<UserData*> user;
TimeId date = 0; TimeId date = 0;
TimeId lastActive = 0;
TimeId lastActivePrecise = 0;
uint32 source = 0; uint32 source = 0;
bool muted = false; bool muted = false;
bool canSelfUnmute = false; bool canSelfUnmute = false;
}; };
struct ParticipantUpdate {
Participant participant;
bool removed = false;
};
[[nodiscard]] auto participants() const [[nodiscard]] auto participants() const
-> const std::vector<Participant> &; -> const std::vector<Participant> &;
[[nodiscard]] const base::flat_set<uint32> &sources() const;
void requestParticipants(); void requestParticipants();
[[nodiscard]] bool participantsLoaded() const; [[nodiscard]] bool participantsLoaded() const;
[[nodiscard]] rpl::producer<> participantsSliceAdded();
[[nodiscard]] rpl::producer<ParticipantUpdate> participantUpdated() const;
void applyUpdate(const MTPGroupCall &update); void applyUpdate(const MTPGroupCall &update);
void applyUpdate(const MTPDupdateGroupCallParticipants &update); void applyUpdate(const MTPDupdateGroupCallParticipants &update);
void applyUpdateChecked(
const MTPDupdateGroupCallParticipants &update);
[[nodiscard]] int fullCount() const; [[nodiscard]] int fullCount() const;
[[nodiscard]] rpl::producer<int> fullCountValue() const;
void reload(); void reload();
[[nodiscard]] bool finished() const; [[nodiscard]] bool finished() const;
@ -46,7 +57,11 @@ public:
private: private:
void applyCall(const MTPGroupCall &call, bool force); void applyCall(const MTPGroupCall &call, bool force);
void applyParticipantsSlice(const QVector<MTPGroupCallParticipant> &list); void applyParticipantsSlice(
const QVector<MTPGroupCallParticipant> &list,
bool sendIndividualUpdates = false);
void applyParticipantsMutes(
const MTPDupdateGroupCallParticipants &update);
const not_null<ChannelData*> _channel; const not_null<ChannelData*> _channel;
const uint64 _id = 0; const uint64 _id = 0;
@ -57,9 +72,12 @@ private:
mtpRequestId _reloadRequestId = 0; mtpRequestId _reloadRequestId = 0;
std::vector<Participant> _participants; std::vector<Participant> _participants;
base::flat_set<uint32> _sources;
QString _nextOffset; QString _nextOffset;
int _fullCount = 0; rpl::variable<int> _fullCount = 0;
rpl::event_stream<ParticipantUpdate> _participantUpdates;
rpl::event_stream<> _participantsSliceAdded;
int _duration = 0; int _duration = 0;
bool _finished = false; bool _finished = false;
bool _allReceived = false; bool _allReceived = false;

@ -1 +1 @@
Subproject commit d2c6ad40d717e604859589d854b81229abd11763 Subproject commit ac86c0ee86293d6fced7dcb48a8c93657f18a7f3