aboutsummaryrefslogtreecommitdiff
path: root/src/widgets
diff options
context:
space:
mode:
authorSalanto <62221668+Salanto@users.noreply.github.com>2024-07-12 11:48:01 +0200
committerGitHub <noreply@github.com>2024-07-12 11:48:01 +0200
commitfb64ca386c51cc3942e1f38cfd76132b1b50e9db (patch)
treef805cd22b191a028312e7f546b571fb35488fa50 /src/widgets
parentc745d0a1b78732550ae790fee12bd23b71a4f25e (diff)
Add playerlist widget element (#996)
* Commit * Boyfailure code commit * Cooking code spaghetti * Accidental overwrite recursive function call hell * Implemented player list * Add partial moderator widget Sleepy time! Hee-Hoo! * Moderator Dialog - Step 1 - WIP * Appease the clang gods * Clang appeasement policy * *sacrifices goat to clang* * Added player report, reworked implementation, ... * Added player-specific report * Reworked implementation * No longer uses JSON. * Removed preset loader. --------- Co-authored-by: TrickyLeifa <date.epoch@gmail.com> Co-authored-by: Leifa <26681464+TrickyLeifa@users.noreply.github.com>
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/moderator_dialog.cpp102
-rw-r--r--src/widgets/moderator_dialog.h38
-rw-r--r--src/widgets/playerlistwidget.cpp169
-rw-r--r--src/widgets/playerlistwidget.h36
4 files changed, 345 insertions, 0 deletions
diff --git a/src/widgets/moderator_dialog.cpp b/src/widgets/moderator_dialog.cpp
new file mode 100644
index 00000000..11b99ccf
--- /dev/null
+++ b/src/widgets/moderator_dialog.cpp
@@ -0,0 +1,102 @@
+#include "moderator_dialog.h"
+
+#include "aoapplication.h"
+#include "gui_utils.h"
+#include "options.h"
+
+#include <QDebug>
+#include <QFile>
+#include <QMessageBox>
+#include <QUiLoader>
+#include <QVBoxLayout>
+
+const QString ModeratorDialog::UI_FILE_PATH = "moderator_action_dialog.ui";
+
+ModeratorDialog::ModeratorDialog(int clientId, bool ban, AOApplication *ao_app, QWidget *parent)
+ : QWidget{parent}
+ , ao_app(ao_app)
+ , m_client_id(clientId)
+ , m_ban(ban)
+{
+ QFile file(Options::getInstance().getUIAsset(UI_FILE_PATH));
+ if (!file.open(QFile::ReadOnly))
+ {
+ qFatal("Unable to open file %s", qPrintable(file.fileName()));
+ return;
+ }
+
+ QUiLoader loader;
+ ui_widget = loader.load(&file, this);
+ auto layout = new QVBoxLayout(this);
+ layout->addWidget(ui_widget);
+
+ FROM_UI(QComboBox, action);
+ FROM_UI(QSpinBox, duration);
+ FROM_UI(QLabel, duration_label);
+ FROM_UI(QCheckBox, permanent);
+ FROM_UI(QTextEdit, details);
+ FROM_UI(QDialogButtonBox, button_box);
+
+ if (m_ban)
+ {
+ ui_action->addItem(tr("Ban"));
+ }
+ else
+ {
+ ui_action->addItem(tr("Kick"));
+ }
+
+ ui_duration->setVisible(m_ban);
+ ui_duration_label->setVisible(m_ban);
+ ui_permanent->setVisible(m_ban);
+
+ connect(ui_button_box, &QDialogButtonBox::accepted, this, &ModeratorDialog::onAcceptedClicked);
+ connect(ui_button_box, &QDialogButtonBox::rejected, this, &ModeratorDialog::close);
+}
+
+ModeratorDialog::~ModeratorDialog()
+{}
+
+void ModeratorDialog::onAcceptedClicked()
+{
+ QString reason = ui_details->toPlainText();
+ if (reason.isEmpty())
+ {
+ if (QMessageBox::question(this, tr("Confirmation"), tr("Are you sure you want to confirm without a reason?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
+ {
+ return;
+ }
+ }
+
+ bool permanent = ui_permanent->isChecked();
+ if (permanent)
+ {
+ if (QMessageBox::question(this, tr("Confirmation"), tr("Are you sure you want to ban permanently?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
+ {
+ return;
+ }
+ }
+
+ QStringList arglist;
+ arglist.append(QString::number(m_client_id));
+ if (m_ban)
+ {
+ if (permanent)
+ {
+ arglist.append("-1");
+ }
+ else
+ {
+ arglist.append(QString::number(ui_duration->value()));
+ }
+ }
+ else
+ {
+ arglist.append("0");
+ }
+ arglist.append(reason);
+
+ ao_app->send_server_packet(AOPacket("MA", arglist));
+
+ close();
+}
diff --git a/src/widgets/moderator_dialog.h b/src/widgets/moderator_dialog.h
new file mode 100644
index 00000000..648f979d
--- /dev/null
+++ b/src/widgets/moderator_dialog.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QSpinBox>
+#include <QTextEdit>
+#include <QWidget>
+
+class AOApplication;
+
+class ModeratorDialog : public QWidget
+{
+ Q_OBJECT
+
+public:
+ static const QString UI_FILE_PATH;
+
+ explicit ModeratorDialog(int clientId, bool ban, AOApplication *ao_app, QWidget *parent = nullptr);
+ virtual ~ModeratorDialog();
+
+private:
+ AOApplication *ao_app;
+ int m_client_id;
+ bool m_ban;
+
+ QWidget *ui_widget;
+ QComboBox *ui_action;
+ QSpinBox *ui_duration;
+ QLabel *ui_duration_label;
+ QCheckBox *ui_permanent;
+ QTextEdit *ui_details;
+ QDialogButtonBox *ui_button_box;
+
+private Q_SLOTS:
+ void onAcceptedClicked();
+};
diff --git a/src/widgets/playerlistwidget.cpp b/src/widgets/playerlistwidget.cpp
new file mode 100644
index 00000000..849c62a9
--- /dev/null
+++ b/src/widgets/playerlistwidget.cpp
@@ -0,0 +1,169 @@
+#include "playerlistwidget.h"
+
+#include "aoapplication.h"
+#include "moderation_functions.h"
+#include "widgets/moderator_dialog.h"
+
+#include <QListWidgetItem>
+#include <QMenu>
+
+PlayerListWidget::PlayerListWidget(AOApplication *ao_app, QWidget *parent)
+ : QListWidget(parent)
+ , ao_app(ao_app)
+{
+ setContextMenuPolicy(Qt::CustomContextMenu);
+
+ connect(this, &PlayerListWidget::customContextMenuRequested, this, &PlayerListWidget::onCustomContextMenuRequested);
+}
+
+PlayerListWidget::~PlayerListWidget()
+{}
+
+void PlayerListWidget::registerPlayer(const PlayerRegister &update)
+{
+ switch (update.type)
+ {
+ default:
+ Q_UNREACHABLE();
+ break;
+
+ case PlayerRegister::ADD_PLAYER:
+ addPlayer(update.id);
+ break;
+
+ case PlayerRegister::REMOVE_PLAYER:
+ removePlayer(update.id);
+ break;
+ }
+}
+
+void PlayerListWidget::updatePlayer(const PlayerUpdate &update)
+{
+ PlayerData &player = m_player_map[update.id];
+
+ bool update_icon = false;
+ switch (update.type)
+ {
+ default:
+ Q_UNREACHABLE();
+ break;
+
+ case PlayerUpdate::NAME:
+ player.name = update.data;
+ break;
+
+ case PlayerUpdate::CHARACTER:
+ player.character = update.data;
+ update_icon = true;
+ break;
+
+ case PlayerUpdate::CHARACTER_NAME:
+ player.character_name = update.data;
+ break;
+
+ case PlayerUpdate::AREA_ID:
+ player.area_id = update.data.toInt();
+ break;
+ }
+ updatePlayer(player.id, update_icon);
+
+ filterPlayerList();
+}
+
+void PlayerListWidget::setAuthenticated(bool f_state)
+{
+ m_is_authenticated = f_state;
+}
+
+void PlayerListWidget::onCustomContextMenuRequested(const QPoint &pos)
+{
+ QListWidgetItem *item = itemAt(pos);
+ if (item == nullptr)
+ {
+ return;
+ }
+ int id = item->data(Qt::UserRole).toInt();
+ QString name = item->text();
+
+ QMenu *menu = new QMenu(this);
+ menu->setAttribute(Qt::WA_DeleteOnClose);
+
+ QAction *report_player_action = menu->addAction("Report Player");
+ connect(report_player_action, &QAction::triggered, this, [this, id, name] {
+ auto maybe_reason = call_moderator_support(name);
+ if (maybe_reason.has_value())
+ {
+ ao_app->send_server_packet(AOPacket("ZZ", {maybe_reason.value(), QString::number(id)}));
+ }
+ });
+
+ if (!m_is_authenticated)
+ {
+ QAction *kick_player_action = menu->addAction("Kick");
+ connect(kick_player_action, &QAction::triggered, this, [this, id, name] {
+ ModeratorDialog *dialog = new ModeratorDialog(id, false, ao_app);
+ dialog->setWindowTitle(tr("Kick %1").arg(name));
+ connect(this, &PlayerListWidget::destroyed, dialog, &ModeratorDialog::deleteLater);
+ dialog->show();
+ });
+
+ QAction *ban_player_action = menu->addAction("Ban");
+ connect(ban_player_action, &QAction::triggered, this, [this, id, name] {
+ ModeratorDialog *dialog = new ModeratorDialog(id, true, ao_app);
+ dialog->setWindowTitle(tr("Ban %1").arg(name));
+ connect(this, &PlayerListWidget::destroyed, dialog, &ModeratorDialog::deleteLater);
+ dialog->show();
+ });
+ }
+
+ menu->popup(mapToGlobal(pos));
+}
+
+void PlayerListWidget::addPlayer(int playerId)
+{
+ m_player_map.insert(playerId, PlayerData{.id = playerId});
+ QListWidgetItem *item = new QListWidgetItem(this);
+ item->setData(Qt::UserRole, playerId);
+ m_item_map.insert(playerId, item);
+ updatePlayer(playerId, false);
+}
+
+void PlayerListWidget::removePlayer(int playerId)
+{
+ delete takeItem(row(m_item_map.take(playerId)));
+ m_player_map.remove(playerId);
+}
+
+void PlayerListWidget::filterPlayerList()
+{
+ int area_id = m_player_map.value(ao_app->client_id).area_id;
+ for (int i = 0; i < count(); ++i)
+ {
+ m_item_map[i]->setHidden(m_player_map[i].area_id != area_id);
+ }
+}
+
+void PlayerListWidget::updatePlayer(int playerId, bool updateIcon)
+{
+ PlayerData &data = m_player_map[playerId];
+ QListWidgetItem *item = m_item_map[playerId];
+ item->setText(data.name.isEmpty() ? QObject::tr("Unnamed Player") : data.name);
+ if (data.character.isEmpty())
+ {
+ item->setToolTip(QString());
+ return;
+ }
+
+ QString tooltip = data.character;
+ if (!data.character_name.isEmpty())
+ {
+ tooltip = QObject::tr("%1 aka %2").arg(data.character, data.character_name);
+ }
+
+ item->setToolTip(tooltip);
+
+ if (updateIcon)
+ {
+ item->setIcon(QIcon(ao_app->get_image_suffix(ao_app->get_character_path(data.character, "char_icon"), true)));
+ }
+}
diff --git a/src/widgets/playerlistwidget.h b/src/widgets/playerlistwidget.h
new file mode 100644
index 00000000..e771d7e1
--- /dev/null
+++ b/src/widgets/playerlistwidget.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "datatypes.h"
+
+#include <QList>
+#include <QListWidget>
+#include <QMap>
+
+class AOApplication;
+
+class PlayerListWidget : public QListWidget
+{
+public:
+ explicit PlayerListWidget(AOApplication *ao_app, QWidget *parent = nullptr);
+ virtual ~PlayerListWidget();
+
+ void registerPlayer(const PlayerRegister &update);
+ void updatePlayer(const PlayerUpdate &update);
+
+ void setAuthenticated(bool f_state);
+
+private:
+ AOApplication *ao_app;
+ QMap<int, PlayerData> m_player_map;
+ QMap<int, QListWidgetItem *> m_item_map;
+ bool m_is_authenticated = false;
+
+ void addPlayer(int playerId);
+ void removePlayer(int playerId);
+ void updatePlayer(int playerId, bool updateIcon);
+
+ void filterPlayerList();
+
+private Q_SLOTS:
+ void onCustomContextMenuRequested(const QPoint &pos);
+};