2020-11-25 14:09:59 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "calls/calls_group_members.h"
|
|
|
|
|
|
|
|
#include "calls/calls_group_call.h"
|
|
|
|
#include "data/data_channel.h"
|
|
|
|
#include "data/data_user.h"
|
|
|
|
#include "data/data_changes.h"
|
|
|
|
#include "data/data_group_call.h"
|
|
|
|
#include "ui/widgets/buttons.h"
|
|
|
|
#include "ui/widgets/scroll_area.h"
|
|
|
|
#include "ui/widgets/popup_menu.h"
|
|
|
|
#include "ui/text/text_utilities.h"
|
|
|
|
#include "ui/effects/ripple_animation.h"
|
|
|
|
#include "main/main_session.h"
|
|
|
|
#include "lang/lang_keys.h"
|
2020-11-27 15:51:24 +00:00
|
|
|
#include "facades.h"
|
2020-11-25 14:09:59 +00:00
|
|
|
#include "styles/style_calls.h"
|
|
|
|
|
|
|
|
namespace Calls {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class Row final : public PeerListRow {
|
|
|
|
public:
|
|
|
|
Row(not_null<ChannelData*> channel, not_null<UserData*> user);
|
|
|
|
|
|
|
|
enum class State {
|
|
|
|
Active,
|
|
|
|
Inactive,
|
|
|
|
Muted,
|
|
|
|
};
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
void updateState(const Data::GroupCall::Participant *participant);
|
2020-11-27 15:51:24 +00:00
|
|
|
[[nodiscard]] State state() const {
|
|
|
|
return _state;
|
|
|
|
}
|
2020-11-27 14:50:41 +00:00
|
|
|
|
2020-11-25 14:09:59 +00:00
|
|
|
void addActionRipple(QPoint point, Fn<void()> updateCallback) override;
|
|
|
|
void stopLastActionRipple() override;
|
|
|
|
|
|
|
|
int nameIconWidth() const override {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
QSize actionSize() const override {
|
|
|
|
return QSize(_st->width, _st->height);
|
|
|
|
}
|
2020-11-27 15:51:24 +00:00
|
|
|
bool actionDisabled() const override {
|
|
|
|
return peer()->isSelf() || !_channel->amCreator();
|
|
|
|
}
|
2020-11-25 14:09:59 +00:00
|
|
|
QMargins actionMargins() const override {
|
|
|
|
return QMargins(
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
st::groupCallMemberButtonSkip,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
void paintAction(
|
|
|
|
Painter &p,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
bool selected,
|
|
|
|
bool actionSelected) override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void refreshStatus() override;
|
|
|
|
|
|
|
|
[[nodiscard]] static State ComputeState(
|
|
|
|
not_null<ChannelData*> channel,
|
|
|
|
not_null<UserData*> user);
|
|
|
|
[[nodiscard]] static not_null<const style::IconButton*> ComputeIconStyle(
|
|
|
|
State state);
|
|
|
|
|
|
|
|
State _state = State::Inactive;
|
2020-11-27 15:51:24 +00:00
|
|
|
not_null<ChannelData*> _channel;
|
2020-11-25 14:09:59 +00:00
|
|
|
not_null<const style::IconButton*> _st;
|
|
|
|
|
|
|
|
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
class MembersController final
|
|
|
|
: public PeerListController
|
|
|
|
, public base::has_weak_ptr {
|
|
|
|
public:
|
|
|
|
explicit MembersController(not_null<GroupCall*> call);
|
|
|
|
|
2020-11-27 15:51:24 +00:00
|
|
|
using MuteRequest = GroupMembers::MuteRequest;
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
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();
|
|
|
|
}
|
2020-11-27 15:51:24 +00:00
|
|
|
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
|
2020-11-27 14:50:41 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2020-11-27 15:51:24 +00:00
|
|
|
rpl::event_stream<MuteRequest> _toggleMuteRequests;
|
2020-11-27 14:50:41 +00:00
|
|
|
rpl::variable<int> _fullCount = 1;
|
|
|
|
Ui::BoxPointer _addBox;
|
|
|
|
rpl::lifetime _lifetime;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2020-11-25 14:09:59 +00:00
|
|
|
Row::Row(not_null<ChannelData*> channel, not_null<UserData*> user)
|
|
|
|
: PeerListRow(user)
|
|
|
|
, _state(ComputeState(channel, user))
|
2020-11-27 15:51:24 +00:00
|
|
|
, _channel(channel)
|
2020-11-25 14:09:59 +00:00
|
|
|
, _st(ComputeIconStyle(_state)) {
|
|
|
|
refreshStatus();
|
|
|
|
}
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-11-25 14:09:59 +00:00
|
|
|
void Row::paintAction(
|
|
|
|
Painter &p,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
bool selected,
|
|
|
|
bool actionSelected) {
|
|
|
|
auto size = actionSize();
|
|
|
|
if (_actionRipple) {
|
|
|
|
_actionRipple->paint(
|
|
|
|
p,
|
|
|
|
x + _st->rippleAreaPosition.x(),
|
|
|
|
y + _st->rippleAreaPosition.y(),
|
|
|
|
outerWidth);
|
|
|
|
if (_actionRipple->empty()) {
|
|
|
|
_actionRipple.reset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_st->icon.paintInCenter(
|
|
|
|
p,
|
|
|
|
style::rtlrect(x, y, size.width(), size.height(), outerWidth));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Row::refreshStatus() {
|
|
|
|
setCustomStatus([&] {
|
|
|
|
switch (_state) {
|
|
|
|
case State::Inactive:
|
2020-11-26 13:04:11 +00:00
|
|
|
case State::Muted: return tr::lng_group_call_inactive(tr::now);
|
|
|
|
case State::Active: return tr::lng_group_call_active(tr::now);
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
2020-11-26 13:04:11 +00:00
|
|
|
Unexpected("State in Row::refreshStatus.");
|
2020-11-27 14:50:41 +00:00
|
|
|
}(), (_state == State::Active));
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Row::State Row::ComputeState(
|
|
|
|
not_null<ChannelData*> channel,
|
|
|
|
not_null<UserData*> user) {
|
|
|
|
const auto call = channel->call();
|
|
|
|
if (!call) {
|
|
|
|
return State::Inactive;
|
|
|
|
}
|
|
|
|
const auto &participants = call->participants();
|
|
|
|
const auto i = ranges::find(
|
|
|
|
participants,
|
|
|
|
user,
|
|
|
|
&Data::GroupCall::Participant::user);
|
|
|
|
if (i == end(participants)) {
|
|
|
|
return State::Inactive;
|
|
|
|
}
|
|
|
|
return !i->muted
|
|
|
|
? State::Active
|
|
|
|
: i->canSelfUnmute
|
|
|
|
? State::Inactive
|
|
|
|
: State::Muted;
|
|
|
|
}
|
|
|
|
|
|
|
|
not_null<const style::IconButton*> Row::ComputeIconStyle(
|
|
|
|
State state) {
|
|
|
|
switch (state) {
|
|
|
|
case State::Inactive: return &st::groupCallInactiveButton;
|
|
|
|
case State::Active: return &st::groupCallActiveButton;
|
|
|
|
case State::Muted: return &st::groupCallMutedButton;
|
|
|
|
}
|
|
|
|
Unexpected("State in Row::ComputeIconStyle.");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Row::addActionRipple(QPoint point, Fn<void()> updateCallback) {
|
|
|
|
if (!_actionRipple) {
|
|
|
|
auto mask = Ui::RippleAnimation::ellipseMask(
|
|
|
|
QSize(_st->rippleAreaSize, _st->rippleAreaSize));
|
|
|
|
_actionRipple = std::make_unique<Ui::RippleAnimation>(
|
|
|
|
_st->ripple,
|
|
|
|
std::move(mask),
|
|
|
|
std::move(updateCallback));
|
|
|
|
}
|
|
|
|
_actionRipple->add(point - _st->rippleAreaPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Row::stopLastActionRipple() {
|
|
|
|
if (_actionRipple) {
|
|
|
|
_actionRipple->lastStop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MembersController::MembersController(not_null<GroupCall*> call)
|
|
|
|
: _call(call)
|
|
|
|
, _channel(call->channel()) {
|
2020-11-27 14:50:41 +00:00
|
|
|
setupListChangeViewers(call);
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
void MembersController::setupListChangeViewers(not_null<GroupCall*> call) {
|
2020-11-25 14:09:59 +00:00
|
|
|
const auto channel = call->channel();
|
2020-11-27 14:50:41 +00:00
|
|
|
channel->session().changes().peerFlagsValue(
|
2020-11-25 14:09:59 +00:00
|
|
|
channel,
|
|
|
|
Data::PeerUpdate::Flag::GroupCall
|
2020-11-27 14:50:41 +00:00
|
|
|
) | 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<Data::GroupCall*> real) {
|
|
|
|
subscribeToChanges(real);
|
|
|
|
}, _lifetime);
|
|
|
|
|
|
|
|
call->stateValue(
|
2020-11-25 14:09:59 +00:00
|
|
|
) | rpl::start_with_next([=] {
|
2020-11-27 14:50:41 +00:00
|
|
|
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);
|
2020-11-25 14:09:59 +00:00
|
|
|
}, _lifetime);
|
2020-11-27 14:50:41 +00:00
|
|
|
|
|
|
|
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);
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Main::Session &MembersController::session() const {
|
|
|
|
return _call->channel()->session();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersController::prepare() {
|
|
|
|
delegate()->peerListSetSearchMode(PeerListSearchMode::Disabled);
|
|
|
|
//delegate()->peerListSetTitle(std::move(title));
|
|
|
|
setDescriptionText(tr::lng_contacts_loading(tr::now));
|
|
|
|
setSearchNoResultsText(tr::lng_blocked_list_not_found(tr::now));
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
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();
|
|
|
|
}
|
2020-11-25 14:09:59 +00:00
|
|
|
loadMoreRows();
|
|
|
|
}
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
void MembersController::prepareRows(not_null<Data::GroupCall*> real) {
|
2020-11-25 14:09:59 +00:00
|
|
|
auto foundSelf = false;
|
|
|
|
auto changed = false;
|
|
|
|
const auto &participants = real->participants();
|
|
|
|
auto count = delegate()->peerListFullRowsCount();
|
|
|
|
for (auto i = 0; i != count;) {
|
|
|
|
auto row = delegate()->peerListRowAt(i);
|
|
|
|
auto user = row->peer()->asUser();
|
|
|
|
if (user->isSelf()) {
|
|
|
|
foundSelf = true;
|
2020-11-26 16:01:26 +00:00
|
|
|
++i;
|
|
|
|
continue;
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
const auto contains = ranges::contains(
|
|
|
|
participants,
|
|
|
|
not_null{ user },
|
|
|
|
&Data::GroupCall::Participant::user);
|
|
|
|
if (contains) {
|
|
|
|
++i;
|
|
|
|
} else {
|
|
|
|
changed = true;
|
|
|
|
delegate()->peerListRemoveRow(row);
|
|
|
|
--count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!foundSelf) {
|
2020-11-27 14:50:41 +00:00
|
|
|
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) {
|
2020-11-25 14:09:59 +00:00
|
|
|
changed = true;
|
|
|
|
delegate()->peerListAppendRow(std::move(row));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const auto &participant : participants) {
|
2020-11-27 14:50:41 +00:00
|
|
|
if (auto row = createRow(participant)) {
|
2020-11-25 14:09:59 +00:00
|
|
|
changed = true;
|
|
|
|
delegate()->peerListAppendRow(std::move(row));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (changed) {
|
|
|
|
delegate()->peerListRefreshRows();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MembersController::loadMoreRows() {
|
2020-11-27 14:50:41 +00:00
|
|
|
if (const auto real = _channel->call()) {
|
|
|
|
real->requestParticipants();
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-27 15:51:24 +00:00
|
|
|
auto MembersController::toggleMuteRequests() const
|
|
|
|
-> rpl::producer<GroupMembers::MuteRequest> {
|
|
|
|
return _toggleMuteRequests.events();
|
|
|
|
}
|
2020-11-25 14:09:59 +00:00
|
|
|
|
2020-11-27 15:51:24 +00:00
|
|
|
void MembersController::rowClicked(not_null<PeerListRow*> row) {
|
|
|
|
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MembersController::rowActionClicked(
|
|
|
|
not_null<PeerListRow*> row) {
|
|
|
|
Expects(row->peer()->isUser());
|
|
|
|
|
2020-11-27 15:51:24 +00:00
|
|
|
const auto real = static_cast<Row*>(row.get());
|
|
|
|
const auto mute = (real->state() != Row::State::Muted);
|
|
|
|
_toggleMuteRequests.fire(MuteRequest{
|
|
|
|
.user = row->peer()->asUser(),
|
|
|
|
.mute = mute,
|
|
|
|
});
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
|
|
|
|
QWidget *parent,
|
|
|
|
not_null<PeerListRow*> row) {
|
|
|
|
Expects(row->peer()->isUser());
|
|
|
|
|
|
|
|
const auto user = row->peer()->asUser();
|
2020-11-26 16:01:26 +00:00
|
|
|
return nullptr;
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 14:50:41 +00:00
|
|
|
std::unique_ptr<PeerListRow> MembersController::createSelfRow() const {
|
|
|
|
const auto self = _channel->session().user();
|
|
|
|
auto result = std::make_unique<Row>(_channel, self);
|
|
|
|
updateRow(result.get(), nullptr);
|
|
|
|
return result;
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<PeerListRow> MembersController::createRow(
|
2020-11-27 14:50:41 +00:00
|
|
|
const Data::GroupCall::Participant &participant) const {
|
|
|
|
auto result = std::make_unique<Row>(_channel, participant.user);
|
|
|
|
updateRow(result.get(), &participant);
|
|
|
|
return result;
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
GroupMembers::GroupMembers(
|
|
|
|
QWidget *parent,
|
|
|
|
not_null<GroupCall*> call)
|
|
|
|
: RpWidget(parent)
|
|
|
|
, _call(call)
|
|
|
|
, _scroll(this, st::defaultSolidScroll)
|
|
|
|
, _listController(std::make_unique<MembersController>(call)) {
|
|
|
|
setupHeader(call);
|
|
|
|
setupList();
|
|
|
|
setContent(_list);
|
|
|
|
_listController->setDelegate(static_cast<PeerListDelegate*>(this));
|
|
|
|
|
|
|
|
paintRequest(
|
|
|
|
) | rpl::start_with_next([=](QRect clip) {
|
|
|
|
QPainter(this).fillRect(clip, st::groupCallMembersBg);
|
|
|
|
}, lifetime());
|
|
|
|
}
|
|
|
|
|
2020-11-27 15:51:24 +00:00
|
|
|
auto GroupMembers::toggleMuteRequests() const
|
|
|
|
-> rpl::producer<GroupMembers::MuteRequest> {
|
|
|
|
return static_cast<MembersController*>(
|
|
|
|
_listController.get())->toggleMuteRequests();
|
|
|
|
}
|
|
|
|
|
2020-11-25 14:09:59 +00:00
|
|
|
int GroupMembers::desiredHeight() const {
|
|
|
|
auto desired = _header ? _header->height() : 0;
|
|
|
|
auto count = [this] {
|
|
|
|
if (const auto call = _call.get()) {
|
|
|
|
if (const auto real = call->channel()->call()) {
|
|
|
|
return real->fullCount();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}();
|
2020-11-26 11:04:00 +00:00
|
|
|
desired += std::max(count, _list->fullRowsCount())
|
2020-11-25 14:09:59 +00:00
|
|
|
* st::groupCallMembersList.item.height;
|
2020-11-27 16:55:18 +00:00
|
|
|
return desired;
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl::producer<int> GroupMembers::desiredHeightValue() const {
|
|
|
|
const auto controller = static_cast<MembersController*>(
|
|
|
|
_listController.get());
|
|
|
|
return rpl::combine(
|
|
|
|
heightValue(),
|
|
|
|
controller->fullCountValue()
|
|
|
|
) | rpl::map([=] {
|
|
|
|
return desiredHeight();
|
|
|
|
});
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::setupHeader(not_null<GroupCall*> call) {
|
|
|
|
_header = object_ptr<Ui::FixedHeightWidget>(
|
|
|
|
this,
|
|
|
|
st::groupCallMembersHeader);
|
|
|
|
auto parent = _header.data();
|
|
|
|
|
|
|
|
_titleWrap = Ui::CreateChild<Ui::RpWidget>(parent);
|
|
|
|
_title = setupTitle(call);
|
|
|
|
_addMember = Ui::CreateChild<Ui::IconButton>(
|
|
|
|
parent,
|
|
|
|
st::groupCallAddMember);
|
|
|
|
setupButtons();
|
|
|
|
|
|
|
|
widthValue(
|
|
|
|
) | rpl::start_with_next([this](int width) {
|
|
|
|
_header->resizeToWidth(width);
|
|
|
|
}, _header->lifetime());
|
|
|
|
}
|
|
|
|
|
|
|
|
object_ptr<Ui::FlatLabel> GroupMembers::setupTitle(
|
|
|
|
not_null<GroupCall*> call) {
|
2020-11-27 14:50:41 +00:00
|
|
|
const auto controller = static_cast<MembersController*>(
|
|
|
|
_listController.get());
|
2020-11-25 14:09:59 +00:00
|
|
|
auto result = object_ptr<Ui::FlatLabel>(
|
|
|
|
_titleWrap,
|
|
|
|
tr::lng_chat_status_members(
|
|
|
|
lt_count_decimal,
|
2020-11-27 14:50:41 +00:00
|
|
|
controller->fullCountValue() | tr::to_count(),
|
2020-11-25 14:09:59 +00:00
|
|
|
Ui::Text::Upper
|
|
|
|
),
|
|
|
|
st::groupCallHeaderLabel);
|
|
|
|
result->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::setupButtons() {
|
|
|
|
using namespace rpl::mappers;
|
|
|
|
|
|
|
|
_addMember->showOn(rpl::single(true));
|
|
|
|
_addMember->addClickHandler([=] { // TODO throttle(ripple duration)
|
|
|
|
addMember();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::setupList() {
|
|
|
|
auto topSkip = _header ? _header->height() : 0;
|
|
|
|
_list = _scroll->setOwnedWidget(object_ptr<ListWidget>(
|
|
|
|
this,
|
|
|
|
_listController.get(),
|
|
|
|
st::groupCallMembersList));
|
|
|
|
|
|
|
|
sizeValue(
|
|
|
|
) | rpl::start_with_next([=](QSize size) {
|
|
|
|
_scroll->setGeometry(0, topSkip, size.width(), size.height() - topSkip);
|
|
|
|
_list->resizeToWidth(size.width());
|
|
|
|
}, _list->lifetime());
|
|
|
|
|
|
|
|
_list->heightValue(
|
|
|
|
) | rpl::start_with_next([=](int listHeight) {
|
|
|
|
auto newHeight = (listHeight > 0)
|
|
|
|
? (topSkip + listHeight)
|
|
|
|
: 0;
|
|
|
|
resize(width(), newHeight);
|
|
|
|
}, _list->lifetime());
|
|
|
|
_list->moveToLeft(0, topSkip);
|
|
|
|
_list->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::resizeEvent(QResizeEvent *e) {
|
|
|
|
if (_header) {
|
|
|
|
updateHeaderControlsGeometry(width());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::updateHeaderControlsGeometry(int newWidth) {
|
|
|
|
auto availableWidth = newWidth
|
|
|
|
- st::groupCallAddButtonPosition.x();
|
|
|
|
_addMember->moveToLeft(
|
|
|
|
availableWidth - _addMember->width(),
|
|
|
|
st::groupCallAddButtonPosition.y(),
|
|
|
|
newWidth);
|
|
|
|
if (!_addMember->isHidden()) {
|
|
|
|
availableWidth -= _addMember->width();
|
|
|
|
}
|
|
|
|
|
|
|
|
_titleWrap->resize(
|
|
|
|
availableWidth - _addMember->width() - st::groupCallHeaderPosition.x(),
|
|
|
|
_title->height());
|
|
|
|
_titleWrap->moveToLeft(
|
|
|
|
st::groupCallHeaderPosition.x(),
|
|
|
|
st::groupCallHeaderPosition.y(),
|
|
|
|
newWidth);
|
|
|
|
_titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
|
|
|
|
_title->resizeToWidth(_titleWrap->width());
|
|
|
|
_title->moveToLeft(0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::addMember() {
|
|
|
|
// #TODO calls
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::visibleTopBottomUpdated(
|
|
|
|
int visibleTop,
|
|
|
|
int visibleBottom) {
|
|
|
|
setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListSetTitle(rpl::producer<QString> title) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListSetAdditionalTitle(rpl::producer<QString> title) {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GroupMembers::peerListIsRowChecked(not_null<PeerListRow*> row) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListScrollToTop() {
|
|
|
|
}
|
|
|
|
|
|
|
|
int GroupMembers::peerListSelectedRowsCount() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<not_null<PeerData*>> GroupMembers::peerListCollectSelectedRows() {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) {
|
2020-11-26 13:04:11 +00:00
|
|
|
Unexpected("Item selection in Calls::GroupMembers.");
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) {
|
2020-11-26 13:04:11 +00:00
|
|
|
Unexpected("Item selection in Calls::GroupMembers.");
|
2020-11-25 14:09:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListFinishSelectedRowsBunch() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void GroupMembers::peerListSetDescription(
|
|
|
|
object_ptr<Ui::FlatLabel> description) {
|
|
|
|
description.destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Calls
|