diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 12affbd858..9eab29b3eb 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -99,6 +99,8 @@ add_executable(citra-qt multiplayer/lobby.cpp multiplayer/message.h multiplayer/message.cpp + multiplayer/moderation_dialog.cpp + multiplayer/moderation_dialog.h multiplayer/state.cpp multiplayer/state.h multiplayer/validation.h @@ -135,6 +137,7 @@ set(UIS multiplayer/chat_room.ui multiplayer/client_room.ui multiplayer/host_room.ui + multiplayer/moderation_dialog.ui aboutdialog.ui cheats.ui hotkeys.ui diff --git a/src/citra_qt/multiplayer/client_room.cpp b/src/citra_qt/multiplayer/client_room.cpp index 16f9a58d7d..54b4dc55e1 100644 --- a/src/citra_qt/multiplayer/client_room.cpp +++ b/src/citra_qt/multiplayer/client_room.cpp @@ -13,6 +13,7 @@ #include "citra_qt/game_list_p.h" #include "citra_qt/multiplayer/client_room.h" #include "citra_qt/multiplayer/message.h" +#include "citra_qt/multiplayer/moderation_dialog.h" #include "citra_qt/multiplayer/state.h" #include "common/logging/log.h" #include "core/announce_multiplayer_session.h" @@ -42,11 +43,23 @@ ClientRoomWindow::ClientRoomWindow(QWidget* parent) connect(ui->disconnect, &QPushButton::pressed, [this] { Disconnect(); }); ui->disconnect->setDefault(false); ui->disconnect->setAutoDefault(false); + connect(ui->moderation, &QPushButton::clicked, [this] { + ModerationDialog dialog(this); + dialog.exec(); + }); + ui->moderation->setDefault(false); + ui->moderation->setAutoDefault(false); UpdateView(); } ClientRoomWindow::~ClientRoomWindow() = default; +void ClientRoomWindow::SetModPerms(bool is_mod) { + ui->moderation->setVisible(is_mod); + ui->moderation->setDefault(false); + ui->moderation->setAutoDefault(false); +} + void ClientRoomWindow::RetranslateUi() { ui->retranslateUi(this); ui->chat->RetranslateUi(); diff --git a/src/citra_qt/multiplayer/client_room.h b/src/citra_qt/multiplayer/client_room.h index 47add6f518..7d4f2b238d 100644 --- a/src/citra_qt/multiplayer/client_room.h +++ b/src/citra_qt/multiplayer/client_room.h @@ -18,6 +18,7 @@ public: ~ClientRoomWindow(); void RetranslateUi(); + void SetModPerms(bool is_mod); public slots: void OnRoomUpdate(const Network::RoomInformation&); diff --git a/src/citra_qt/multiplayer/client_room.ui b/src/citra_qt/multiplayer/client_room.ui index 22b969d3b4..97e88b502d 100644 --- a/src/citra_qt/multiplayer/client_room.ui +++ b/src/citra_qt/multiplayer/client_room.ui @@ -41,6 +41,16 @@ + + + + Moderation... + + + false + + + diff --git a/src/citra_qt/multiplayer/moderation_dialog.cpp b/src/citra_qt/multiplayer/moderation_dialog.cpp new file mode 100644 index 0000000000..def084666a --- /dev/null +++ b/src/citra_qt/multiplayer/moderation_dialog.cpp @@ -0,0 +1,113 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "citra_qt/multiplayer/moderation_dialog.h" +#include "network/network.h" +#include "network/room_member.h" +#include "ui_moderation_dialog.h" + +namespace Column { +enum { + SUBJECT, + TYPE, + COUNT, +}; +} + +ModerationDialog::ModerationDialog(QWidget* parent) + : QDialog(parent), ui(std::make_unique()) { + ui->setupUi(this); + + qRegisterMetaType(); + + if (auto member = Network::GetRoomMember().lock()) { + callback_handle_status_message = member->BindOnStatusMessageReceived( + [this](const Network::StatusMessageEntry& status_message) { + emit StatusMessageReceived(status_message); + }); + connect(this, &ModerationDialog::StatusMessageReceived, this, + &ModerationDialog::OnStatusMessageReceived); + callback_handle_ban_list = member->BindOnBanListReceived( + [this](const Network::Room::BanList& ban_list) { emit BanListReceived(ban_list); }); + connect(this, &ModerationDialog::BanListReceived, this, &ModerationDialog::PopulateBanList); + } + + // Initialize the UI + model = new QStandardItemModel(ui->ban_list_view); + model->insertColumns(0, Column::COUNT); + model->setHeaderData(Column::SUBJECT, Qt::Horizontal, tr("Subject")); + model->setHeaderData(Column::TYPE, Qt::Horizontal, tr("Type")); + + ui->ban_list_view->setModel(model); + + // Load the ban list in background + LoadBanList(); + + connect(ui->refresh, &QPushButton::clicked, this, [this] { LoadBanList(); }); + connect(ui->unban, &QPushButton::clicked, this, [this] { + auto index = ui->ban_list_view->currentIndex(); + SendUnbanRequest(model->item(index.row(), 0)->text()); + }); + connect(ui->ban_list_view, &QTreeView::clicked, [this] { ui->unban->setEnabled(true); }); +} + +ModerationDialog::~ModerationDialog() { + if (callback_handle_status_message) { + if (auto room = Network::GetRoomMember().lock()) { + room->Unbind(callback_handle_status_message); + } + } + + if (callback_handle_ban_list) { + if (auto room = Network::GetRoomMember().lock()) { + room->Unbind(callback_handle_ban_list); + } + } +} + +void ModerationDialog::LoadBanList() { + if (auto room = Network::GetRoomMember().lock()) { + ui->refresh->setEnabled(false); + ui->refresh->setText(tr("Refreshing")); + ui->unban->setEnabled(false); + room->RequestBanList(); + } +} + +void ModerationDialog::PopulateBanList(const Network::Room::BanList& ban_list) { + model->removeRows(0, model->rowCount()); + for (const auto& username : ban_list.first) { + QStandardItem* subject_item = new QStandardItem(QString::fromStdString(username)); + QStandardItem* type_item = new QStandardItem(tr("Forum Username")); + model->invisibleRootItem()->appendRow({subject_item, type_item}); + } + for (const auto& ip : ban_list.second) { + QStandardItem* subject_item = new QStandardItem(QString::fromStdString(ip)); + QStandardItem* type_item = new QStandardItem(tr("IP Address")); + model->invisibleRootItem()->appendRow({subject_item, type_item}); + } + for (int i = 0; i < Column::COUNT - 1; ++i) { + ui->ban_list_view->resizeColumnToContents(i); + } + ui->refresh->setEnabled(true); + ui->refresh->setText(tr("Refresh")); + ui->unban->setEnabled(false); +} + +void ModerationDialog::SendUnbanRequest(const QString& subject) { + if (auto room = Network::GetRoomMember().lock()) { + room->SendModerationRequest(Network::IdModUnban, subject.toStdString()); + } +} + +void ModerationDialog::OnStatusMessageReceived(const Network::StatusMessageEntry& status_message) { + if (status_message.type != Network::IdMemberBanned && + status_message.type != Network::IdAddressUnbanned) + return; + + // Update the ban list for ban/unban + LoadBanList(); +} diff --git a/src/citra_qt/multiplayer/moderation_dialog.h b/src/citra_qt/multiplayer/moderation_dialog.h new file mode 100644 index 0000000000..d10083d5b8 --- /dev/null +++ b/src/citra_qt/multiplayer/moderation_dialog.h @@ -0,0 +1,42 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "network/room.h" +#include "network/room_member.h" + +namespace Ui { +class ModerationDialog; +} + +class QStandardItemModel; + +class ModerationDialog : public QDialog { + Q_OBJECT + +public: + explicit ModerationDialog(QWidget* parent = nullptr); + ~ModerationDialog(); + +signals: + void StatusMessageReceived(const Network::StatusMessageEntry&); + void BanListReceived(const Network::Room::BanList&); + +private: + void LoadBanList(); + void PopulateBanList(const Network::Room::BanList& ban_list); + void SendUnbanRequest(const QString& subject); + void OnStatusMessageReceived(const Network::StatusMessageEntry& status_message); + + std::unique_ptr ui; + QStandardItemModel* model; + Network::RoomMember::CallbackHandle callback_handle_status_message; + Network::RoomMember::CallbackHandle callback_handle_ban_list; +}; + +Q_DECLARE_METATYPE(Network::Room::BanList); diff --git a/src/citra_qt/multiplayer/moderation_dialog.ui b/src/citra_qt/multiplayer/moderation_dialog.ui new file mode 100644 index 0000000000..808d994142 --- /dev/null +++ b/src/citra_qt/multiplayer/moderation_dialog.ui @@ -0,0 +1,84 @@ + + + ModerationDialog + + + Moderation + + + + 0 + 0 + 500 + 300 + + + + + + + Ban List + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Refreshing + + + false + + + + + + + Unban + + + false + + + + + + + + + + + + + + + QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + ModerationDialog + accept() + + + + diff --git a/src/citra_qt/multiplayer/state.cpp b/src/citra_qt/multiplayer/state.cpp index 14b2bd7ecd..996af55b33 100644 --- a/src/citra_qt/multiplayer/state.cpp +++ b/src/citra_qt/multiplayer/state.cpp @@ -226,6 +226,12 @@ void MultiplayerState::OnOpenNetworkRoom() { if (client_room == nullptr) { client_room = new ClientRoomWindow(this); } + const std::string host_username = member->GetRoomInformation().host_username; + if (host_username.empty()) { + client_room->SetModPerms(false); + } else { + client_room->SetModPerms(member->GetUsername() == host_username); + } BringWidgetToFront(client_room); return; } diff --git a/src/citra_qt/multiplayer/state.h b/src/citra_qt/multiplayer/state.h index 78a36865bf..a49288e834 100644 --- a/src/citra_qt/multiplayer/state.h +++ b/src/citra_qt/multiplayer/state.h @@ -66,6 +66,7 @@ private: QAction* show_room; std::shared_ptr announce_multiplayer_session; Network::RoomMember::State current_state = Network::RoomMember::State::Uninitialized; + bool has_mod_perms = false; Network::RoomMember::CallbackHandle state_callback_handle; Network::RoomMember::CallbackHandle error_callback_handle; };