Add member menu on click in group call members list.

This commit is contained in:
John Preston 2020-11-30 14:31:32 +03:00
parent 834516d4a7
commit 53052c6140
7 changed files with 207 additions and 28 deletions

View File

@ -1846,6 +1846,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_group_call_members#other" = "{count} members";
"lng_group_call_no_anonymous" = "Anonymous admins can't join voice chats :(";
"lng_group_call_context_mute" = "Mute";
"lng_group_call_context_unmute" = "Unmute";
"lng_no_mic_permission" = "Telegram needs access to your microphone so that you can make calls and record voice messages.";

View File

@ -409,6 +409,36 @@ groupCallHeight: 580px;
groupCallRipple: RippleAnimation(defaultRippleAnimation) {
color: groupCallMembersBgRipple;
}
groupCallMenu: Menu(defaultMenu) {
itemBg: groupCallMembersBgOver;
itemBgOver: groupCallMembersBgRipple;
itemFg: groupCallMembersFg;
itemFgOver: groupCallMembersFg;
itemFgDisabled: groupCallMemberNotJoinedStatus;
itemFgShortcut: groupCallMemberNotJoinedStatus;
itemFgShortcutOver: groupCallMemberNotJoinedStatus;
itemFgShortcutDisabled: groupCallMemberNotJoinedStatus;
separatorFg: groupCallMemberNotJoinedStatus;
arrow: icon {{ "dropdown_submenu_arrow", groupCallMemberNotJoinedStatus }};
ripple: groupCallRipple;
}
groupCallMenuShadow: Shadow(defaultEmptyShadow) {
fallback: groupCallMembersBgOver;
}
groupCallPanelAnimation: PanelAnimation(defaultPanelAnimation) {
fadeBg: groupCallMembersBgOver;
shadow: groupCallMenuShadow;
}
groupCallPopupMenu: PopupMenu(defaultPopupMenu) {
shadow: groupCallMenuShadow;
menu: groupCallMenu;
animation: groupCallPanelAnimation;
}
groupCallMembersListItem: PeerListItem(defaultPeerListItem) {
button: OutlineButton(defaultPeerListButton) {
textBg: groupCallMembersBg;
@ -470,6 +500,7 @@ groupCallMultiSelect: MultiSelect(defaultMultiSelect) {
placeholderFg: groupCallMemberNotJoinedStatus;
placeholderFgActive: groupCallMemberNotJoinedStatus;
placeholderFgError: groupCallMemberNotJoinedStatus;
menu: groupCallPopupMenu;
}
fieldIcon: icon {{ "box_search", groupCallMemberNotJoinedStatus, point(10px, 9px) }};
fieldCancel: CrossButton(defaultMultiSelectSearchCancel) {
@ -559,9 +590,7 @@ groupCallBox: Box(defaultBox) {
textBg: groupCallMembersBg;
textBgOver: groupCallMembersBgOver;
ripple: RippleAnimation(defaultRippleAnimation) {
color: groupCallMembersBgRipple;
}
ripple: groupCallRipple;
}
margin: margins(0px, 56px, 0px, 10px);
bg: groupCallMembersBg;
@ -612,9 +641,7 @@ groupCallSettingsButton: SettingsButton {
height: 20px;
padding: margins(22px, 10px, 22px, 8px);
ripple: RippleAnimation(defaultRippleAnimation) {
color: groupCallMembersBgRipple;
}
ripple: groupCallRipple;
}
groupCallSettingsAttentionButton: SettingsButton(groupCallSettingsButton) {
textFg: attentionButtonFg;

View File

@ -18,12 +18,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h"
#include "ui/text/text_utilities.h"
#include "ui/effects/ripple_animation.h"
#include "core/application.h" // Core::App().domain, Core::App().activeWindow.
#include "main/main_domain.h" // Core::App().domain().activate.
#include "main/main_session.h"
#include "base/timer.h"
#include "boxes/peers/edit_participants_box.h"
#include "lang/lang_keys.h"
#include "facades.h" // Ui::showPeerHistory.
#include "mainwindow.h" // App::wnd()->activate.
#include "window/window_controller.h" // Controller::sessionController.
#include "window/window_session_controller.h"
#include "styles/style_calls.h"
namespace Calls {
@ -107,7 +109,9 @@ class MembersController final
: public PeerListController
, public base::has_weak_ptr {
public:
explicit MembersController(not_null<GroupCall*> call);
MembersController(
not_null<GroupCall*> call,
not_null<QWidget*> menuParent);
using MuteRequest = GroupMembers::MuteRequest;
@ -124,6 +128,8 @@ public:
return _fullCount.value();
}
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>;
private:
[[nodiscard]] std::unique_ptr<PeerListRow> createSelfRow() const;
@ -155,8 +161,11 @@ private:
uint64 _realId = 0;
rpl::event_stream<MuteRequest> _toggleMuteRequests;
rpl::event_stream<not_null<UserData*>> _kickMemberRequests;
rpl::variable<int> _fullCount = 1;
Ui::BoxPointer _addBox;
not_null<QWidget*> _menuParent;
base::unique_qptr<Ui::PopupMenu> _menu;
//base::flat_map<not_null<UserData*>, crl::time> _repaintByTimer;
//base::Timer _repaintTimer;
@ -318,9 +327,12 @@ void Row::stopLastActionRipple() {
}
}
MembersController::MembersController(not_null<GroupCall*> call)
MembersController::MembersController(
not_null<GroupCall*> call,
not_null<QWidget*> menuParent)
: _call(call)
, _channel(call->channel()) {
, _channel(call->channel())
, _menuParent(menuParent) {
//, _repaintTimer([=] { repaintByTimer(); }) {
setupListChangeViewers(call);
}
@ -586,25 +598,27 @@ void MembersController::loadMoreRows() {
}
auto MembersController::toggleMuteRequests() const
-> rpl::producer<GroupMembers::MuteRequest> {
-> rpl::producer<MuteRequest> {
return _toggleMuteRequests.events();
}
auto MembersController::kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>{
return _kickMemberRequests.events();
}
void MembersController::rowClicked(not_null<PeerListRow*> row) {
Ui::showPeerHistory(row->peer(), ShowAtUnreadMsgId);
App::wnd()->activate();
if (_menu) {
_menu->deleteLater();
_menu = nullptr;
}
_menu = rowContextMenu(_menuParent, row);
_menu->popup(QCursor::pos());
}
void MembersController::rowActionClicked(
not_null<PeerListRow*> row) {
Expects(row->peer()->isUser());
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,
});
rowClicked(row);
}
base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
@ -612,8 +626,86 @@ base::unique_qptr<Ui::PopupMenu> MembersController::rowContextMenu(
not_null<PeerListRow*> row) {
Expects(row->peer()->isUser());
const auto real = static_cast<Row*>(row.get());
const auto user = row->peer()->asUser();
return nullptr;
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::groupCallPopupMenu);
const auto mute = (real->state() != Row::State::Muted);
const auto toggleMute = crl::guard(this, [=] {
_toggleMuteRequests.fire(MuteRequest{
.user = user,
.mute = mute,
});
});
const auto session = &user->session();
const auto getCurrentWindow = [=]() -> Window::SessionController* {
if (const auto window = Core::App().activeWindow()) {
if (const auto controller = window->sessionController()) {
if (&controller->session() == session) {
return controller;
}
}
}
return nullptr;
};
const auto getWindow = [=] {
if (const auto current = getCurrentWindow()) {
return current;
} else if (&Core::App().domain().active() != &session->account()) {
Core::App().domain().activate(&session->account());
}
return getCurrentWindow();
};
const auto performOnMainWindow = [=](auto callback) {
if (const auto window = getWindow()) {
if (_menu) {
_menu->discardParentReActivate();
// We must hide PopupMenu before we activate the MainWindow,
// otherwise we set focus in field inside MainWindow and then
// PopupMenu::hide activates back the group call panel :(
_menu = nullptr;
}
callback(window);
window->widget()->activate();
}
};
const auto showProfile = [=] {
performOnMainWindow([=](not_null<Window::SessionController*> window) {
window->showPeerInfo(user);
});
};
const auto showHistory = [=] {
performOnMainWindow([=](not_null<Window::SessionController*> window) {
window->showPeerHistory(user);
});
};
const auto removeFromGroup = crl::guard(this, [=] {
_kickMemberRequests.fire_copy(user);
});
if (!user->isSelf() && _channel->canManageCall()) {
result->addAction(
(mute
? tr::lng_group_call_context_mute(tr::now)
: tr::lng_group_call_context_unmute(tr::now)),
toggleMute);
}
result->addAction(
tr::lng_context_view_profile(tr::now),
showProfile);
result->addAction(
tr::lng_context_send_message(tr::now),
showHistory);
if (_channel->canRestrictUser(user)) {
result->addAction(
tr::lng_context_remove_from_group(tr::now),
removeFromGroup);
}
return result;
}
std::unique_ptr<PeerListRow> MembersController::createSelfRow() const {
@ -633,15 +725,16 @@ std::unique_ptr<PeerListRow> MembersController::createRow(
} // namespace
GroupMembers::GroupMembers(
QWidget *parent,
not_null<QWidget*> parent,
not_null<GroupCall*> call)
: RpWidget(parent)
, _call(call)
, _scroll(this, st::defaultSolidScroll)
, _listController(std::make_unique<MembersController>(call)) {
, _listController(std::make_unique<MembersController>(call, parent)) {
setupHeader(call);
setupList();
setContent(_list);
setupFakeRoundCorners();
_listController->setDelegate(static_cast<PeerListDelegate*>(this));
paintRequest(
@ -660,6 +753,12 @@ auto GroupMembers::toggleMuteRequests() const
_listController.get())->toggleMuteRequests();
}
auto GroupMembers::kickMemberRequests() const
-> rpl::producer<not_null<UserData*>> {
return static_cast<MembersController*>(
_listController.get())->kickMemberRequests();
}
int GroupMembers::desiredHeight() const {
auto desired = _header ? _header->height() : 0;
auto count = [&] {
@ -790,6 +889,10 @@ void GroupMembers::updateHeaderControlsGeometry(int newWidth) {
_title->moveToLeft(0, 0);
}
void GroupMembers::setupFakeRoundCorners() {
}
void GroupMembers::visibleTopBottomUpdated(
int visibleTop,
int visibleBottom) {

View File

@ -26,7 +26,7 @@ class GroupMembers final
, private PeerListContentDelegate {
public:
GroupMembers(
QWidget *parent,
not_null<QWidget*> parent,
not_null<GroupCall*> call);
struct MuteRequest {
@ -37,6 +37,8 @@ public:
[[nodiscard]] int desiredHeight() const;
[[nodiscard]] rpl::producer<int> desiredHeightValue() const override;
[[nodiscard]] rpl::producer<MuteRequest> toggleMuteRequests() const;
[[nodiscard]] auto kickMemberRequests() const
-> rpl::producer<not_null<UserData*>>;
[[nodiscard]] rpl::producer<> addMembersRequests() const {
return _addMemberRequests.events();
}
@ -68,6 +70,7 @@ private:
void setupHeader(not_null<GroupCall*> call);
object_ptr<Ui::FlatLabel> setupTitle(not_null<GroupCall*> call);
void setupList();
void setupFakeRoundCorners();
void setupButtons(not_null<GroupCall*> call);

View File

@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/event_filter.h"
#include "boxes/peers/edit_participants_box.h"
#include "app.h"
#include "apiwrap.h" // api().kickParticipant.
#include "styles/style_calls.h"
#include "styles/style_layers.h"
@ -360,6 +361,11 @@ void GroupPanel::initWithCall(GroupCall *call) {
}
}, _callLifetime);
_members->kickMemberRequests(
) | rpl::start_with_next([=](not_null<UserData*> user) {
kickMember(user);
}, _callLifetime);
_members->addMembersRequests(
) | rpl::start_with_next([=] {
if (_call) {
@ -455,6 +461,43 @@ void GroupPanel::addMembers() {
_layerBg->showBox(Box<PeerListBox>(std::move(controller), initBox));
}
void GroupPanel::kickMember(not_null<UserData*> user) {
_layerBg->showBox(Box([=](not_null<Ui::GenericBox*> box) {
box->addRow(
object_ptr<Ui::FlatLabel>(
box.get(),
tr::lng_profile_sure_kick(
tr::now,
lt_user,
user->firstName),
st::groupCallBoxLabel),
style::margins(
st::boxRowPadding.left(),
st::boxPadding.top(),
st::boxRowPadding.right(),
st::boxPadding.bottom()));
box->addButton(tr::lng_box_remove(), [=] {
box->closeBox();
kickMemberSure(user);
});
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}));
}
void GroupPanel::kickMemberSure(not_null<UserData*> user) {
const auto currentRestrictedRights = [&]() -> MTPChatBannedRights {
const auto it = _channel->mgInfo->lastRestricted.find(user);
return (it != _channel->mgInfo->lastRestricted.cend())
? it->second.rights
: MTP_chatBannedRights(MTP_flags(0), MTP_int(0));
}();
_channel->session().api().kickParticipant(
_channel,
user,
currentRestrictedRights);
}
void GroupPanel::initLayout() {
initGeometry();

View File

@ -92,6 +92,8 @@ private:
void hangup(bool discardCallChecked);
void addMembers();
void kickMember(not_null<UserData*> user);
void kickMemberSure(not_null<UserData*> user);
[[nodiscard]] int computeMembersListTop() const;
[[nodiscard]] std::optional<QRect> computeTitleRect() const;
void refreshTitle();

@ -1 +1 @@
Subproject commit 79ea651127962fff366e9d76ce4d932490b30158
Subproject commit e0fb1129d145054410476bd83d1cecf5c2a2644d